@agentcomms/events-daemon 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -1,18 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import { $ as sourcesList, $t as assertControlSupported, A as normaliseWhatsAppSourceOptions, At as decideHeld, B as addTarget, Bt as CONTROL_PROTOCOL_VERSIONS, C as canonicalSseSubscriber, Ct as enableAll, D as normaliseResendSourceOptions, Dt as ACTIVATION_FAILURE, E as normaliseGmailSourceOptions, Et as doctor, F as canonicalJudgeBudget, Ft as catalogueList, G as addSubscriber, Gt as isControlRequest, H as targetsList, Ht as controlFailure, I as canonicalJudgeKindEnablement, It as catalogueShow, J as updateSubscriber, Jt as newInstanceRecord, K as removeSubscriber, Kt as negotiateVersion, L as judgeKindDefinition, Lt as approvalView, M as JUDGE_BUDGET_ID, Mt as dropDelivery, N as JUDGE_KIND_DEFINITIONS, Nt as heldList, O as normaliseSlackSourceOptions, Ot as isActivationFailureCode, P as canonicalJudge, Pt as retryDelivery, Q as sourceShow$1, Qt as writeInstance, R as createEventsMcpServer, Rt as EventControlClient, S as normaliseActivationDocument, St as updateJudge, T as classifySourceOptionChange, Tt as assertDoctorReportV2, U as updateTarget, Ut as encodeControlFrame, V as removeTarget$1, Vt as ControlFrameReader, W as targetTest, Wt as isControlHello, X as status, Xt as readInstance, Y as stop, Yt as processIsLive, Z as loadSqlite, Zt as removeInstance, _ as activationDocumentDigest, _t as judgeTest, an as ensureEventSocketDirectory, at as rulesList, b as canonicalTarget, bt as removeJudgeModel, cn as installErrorSink, ct as ruleTestRetained, d as B3_SECRET_TERMINAL_ORIGIN, dt as addJudge, en as assertSocketPathFits, et as createRule$1, f as isB3ControlOperation, ft as budgetShow, g as isOwnerControlWireOperation, gt as judgeModelStatus, h as OWNER_CONTROL_WIRES, ht as judgeKindsList, in as ensureEventPaths, it as ruleShow$1, j as APPROVED_TYPESAFE_MODELS, jt as deliveriesList, k as normaliseSourceOptions, kt as disableAll, lt as pause, m as renderB3ApprovalPreviewV1, mt as enableJudgeKind, nn as verifyControlSocket, nt as enableRule, on as ensureOwnerOnlyFile, ot as updateRule, p as isB3SecretOperation, pt as disableJudgeKind, q as subscribersList, qt as mayRecoverStaleInstance, rn as verifyPrivateSocketDirectory, rt as removeRule, sn as eventPaths, st as ruleTest, tn as controlEndpoint, tt as disableRule$1, ut as resume, v as canonicalFullRuleDocument, vt as judgesList, w as sseDeliveryTarget, wt as assertDoctorReportV1, x as disclosureBindingFor, xt as updateBudget, y as canonicalSubscriber, yt as removeJudge, z as VERSION, zt as probeControl } from "./b3-contract-C1q5aekF.mjs";
3
- import { a as EventDomainError, d as isJudgeKind, f as validateLocalEndpoint, g as parseIpLiteral, h as assertApprovedAddressSet, i as webhookDeliveryTarget, l as JUDGE_KIND_TYPESAFE, m as assertAddressAllowed, n as canonicalWebhookTarget, o as JUDGE_KINDS, p as AddressPolicyError, r as secretWebhookUrlDescriptor, s as JUDGE_KIND_LAYA, t as canonicalSecretWebhookUrl, u as assertJudgeKindEnabled } from "./webhook-target-DyDTwWhV.mjs";
4
- import { a as WriterQuiescer, c as openEventSecretStore, o as withEventWriterLease, r as RecordStorageError, s as migrateEventSecrets, t as EventRecordCipher } from "./records-DsUxYBwb.mjs";
2
+ import { $ as addSubscriber, $t as isControlHello, A as normaliseSourceOptions, At as updateJudge, B as canonicalJudgeKindEnablement, Bt as deliveriesList, C as normaliseActivationDocument, Ct as judgeKindsList, D as normaliseGmailSourceOptions, Dt as removeJudge, E as classifySourceOptionChange, Et as judgesList, F as assertCreatableLayaJudge, Ft as doctor, G as registerLayaEnablementInputs, Gt as catalogueShow, H as layaEnablementInputsFor, Ht as heldList, I as assertLayaJudgeKindAvailable, It as ACTIVATION_FAILURE, J as addTarget, Jt as probeControl, K as createEventsMcpServer, Kt as approvalView, L as assertLayaRuleJudgeOffered, Lt as isActivationFailureCode, M as APPROVED_TYPESAFE_MODELS, Mt as assertDoctorReportV1, N as JUDGE_BUDGET_ID, Nt as assertDoctorReportV2, O as normaliseResendSourceOptions, Ot as removeJudgeModel, P as JUDGE_KIND_DEFINITIONS, Pt as assertDoctorReportV3, Q as targetTest, Qt as encodeControlFrame, R as canonicalJudge, Rt as disableAll, S as disclosureBindingFor, St as enableJudgeKind, T as sseDeliveryTarget, Tt as judgeTest, U as layaJudgeKindDefinitionFor, Ut as retryDelivery, V as judgeKindDefinition, Vt as dropDelivery, W as layaJudgeTermsFor, Wt as catalogueList, X as targetsList, Xt as ControlFrameReader, Y as removeTarget$1, Yt as CONTROL_PROTOCOL_VERSIONS, Z as updateTarget, Zt as controlFailure, _ as isOwnerControlWireOperation, _t as pause, an as readInstance, at as loadSqlite, b as canonicalSubscriber, bt as budgetShow, cn as assertControlSupported, ct as createRule$1, d as B3_SECRET_TERMINAL_ORIGIN, dn as verifyControlSocket, dt as removeRule, en as isControlRequest, et as removeSubscriber, f as isB3ControlOperation, fn as verifyPrivateSocketDirectory, ft as ruleShow$1, g as OWNER_CONTROL_WIRES, gn as eventPaths, gt as ruleTestRetained, h as layaJudgePreviewFor, hn as ensureOwnerOnlyFile, ht as ruleTest, in as processIsLive, it as status, j as normaliseWhatsAppSourceOptions, jt as enableAll, k as normaliseSlackSourceOptions, kt as updateBudget, ln as assertSocketPathFits, lt as disableRule$1, m as renderB3ApprovalPreviewV1, mn as ensureEventSocketDirectory, mt as updateRule, nn as mayRecoverStaleInstance, nt as updateSubscriber, on as removeInstance, ot as sourceShow$1, p as isB3SecretOperation, pn as ensureEventPaths, pt as rulesList, q as VERSION, qt as EventControlClient, rn as newInstanceRecord, rt as stop, sn as writeInstance, st as sourcesList, tn as negotiateVersion, tt as subscribersList, un as controlEndpoint, ut as enableRule, v as activationDocumentDigest, vt as resume, w as canonicalSseSubscriber, wt as judgeModelStatus, x as canonicalTarget, xt as disableJudgeKind, y as canonicalFullRuleDocument, yt as addJudge, z as canonicalJudgeBudget, zt as decideHeld } from "./b3-contract-CG8Z2OtX.mjs";
3
+ import { a as createLayaWorkerBoundary, d as encodeWorkerFrame, h as isLayaTemplateId, l as WorkerFrameParser, u as createWorkerRequestId, v as installErrorSink } from "./worker-boundary-probe-CFN6EsyK.mjs";
4
+ import { _ as JUDGE_KIND_TYPESAFE, c as validateLocalEndpoint, d as assertApprovedAddressSet, f as parseIpLiteral, h as JUDGE_KIND_LAYA, i as webhookDeliveryTarget, l as AddressPolicyError, m as JUDGE_KINDS, n as canonicalWebhookTarget, p as EventDomainError, r as secretWebhookUrlDescriptor, t as canonicalSecretWebhookUrl, u as assertAddressAllowed, v as assertJudgeKindEnabled, y as isJudgeKind } from "./webhook-target-EzLIFpIY.mjs";
5
+ import { c as migrateEventSecrets, i as RecordStorageError, l as openEventSecretStore, n as LayaModelRepository, o as WriterQuiescer, s as withEventWriterLease, t as EventRecordCipher } from "./records-D15LoVeb.mjs";
5
6
  import process$1 from "node:process";
6
7
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
7
- import { chmod, lstat, open, readFile, rm, writeFile } from "node:fs/promises";
8
+ import { appendFile, chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, rename, rm, statfs, writeFile } from "node:fs/promises";
8
9
  import net, { createServer } from "node:net";
9
10
  import { CommsError, TaintCollector, agentMarker, canPrompt, canonicalJson, escapeForDisplay, hashChallenge, isCommsError, isDangerous, neutralise, newBoundary, openCore, renderFencedBody, resolvePaths, sanitizeHtmlToText, sanitizePlainText, sha256Hex, stripInvisible, wrapUntrusted } from "@agentcomms/core";
10
- import fs, { constants } from "node:fs";
11
- import path from "node:path";
11
+ import fs, { constants, createReadStream } from "node:fs";
12
+ import path, { basename, dirname, join, resolve, sep } from "node:path";
12
13
  import { CATALOGUE, TEST_CLOUD_EVENT, TEST_CLOUD_EVENT_BYTES, applyRepresentation, buildCloudEvent, canonicalJson as canonicalJson$1, catalogueEntry, classifyMapped, cloudEventBytes, compileMapping, conditionPointers, evaluateCondition, evaluateMapping, eventId, eventIdPreimage, expandPattern, getPointer, gmailMessageLabelledV1, gmailMessageReceivedV1, gmailMessageSentV1, parsePointer, validateEvent, whatsappMessageKey } from "@agentcomms/events";
14
+ import childProcess from "node:child_process";
13
15
  import { createInterface } from "node:readline/promises";
14
16
  import { EventEmitter } from "node:events";
15
- import childProcess from "node:child_process";
16
17
  import { stripVTControlCharacters } from "node:util";
17
18
  import { createGmailEventSource, normaliseGmailEventMetadata } from "@agentcomms/gmail";
18
19
  import { createResendEventReaderForPaths } from "@agentcomms/resend";
@@ -20,6 +21,8 @@ import { openSlackEventSourceForPaths } from "@agentcomms/slack";
20
21
  import { chatKindOf, openWhatsAppEventOperations, visibilityUnitKey } from "@agentcomms/whatsapp";
21
22
  import tls from "node:tls";
22
23
  import dns from "node:dns";
24
+ import { Readable } from "node:stream";
25
+ import { createGunzip } from "node:zlib";
23
26
  //#region ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/error.js
24
27
  /**
25
28
  * CommanderError class
@@ -3246,13 +3249,24 @@ function documentRecord(value) {
3246
3249
  return JSON.parse(canonicalJson(value));
3247
3250
  }
3248
3251
  /** SQLite repository for D2's immutable, initially inert version rows. */
3249
- var ImmutableVersions = class {
3252
+ var ImmutableVersions = class ImmutableVersions {
3250
3253
  #database;
3251
- constructor(database) {
3254
+ #layaInputs;
3255
+ #judgeContract = "b3-control-v1";
3256
+ constructor(database, options = layaEnablementInputsFor(database)) {
3252
3257
  this.#database = database;
3258
+ this.#layaInputs = options;
3259
+ }
3260
+ /** The repository over this database's own Laya inputs, creating judges and rules as `contract` defines them. */
3261
+ static forJudgeContract(database, contract) {
3262
+ const versions = new ImmutableVersions(database);
3263
+ versions.#judgeContract = contract;
3264
+ return versions;
3253
3265
  }
3254
3266
  createRule(input) {
3255
3267
  const document = canonicalFullRuleDocument(input);
3268
+ if (this.#judgeContract === "b3-control-v3") assertCreatableLayaJudge(document.agentic?.judge, this.#layaInputs);
3269
+ else this.#assertLayaRuleOffered(document);
3256
3270
  this.assertEmbeddedVersions(document);
3257
3271
  return this.insert("rule_versions", "rule", document.ruleId, document.version, document);
3258
3272
  }
@@ -3272,9 +3286,24 @@ var ImmutableVersions = class {
3272
3286
  const document = canonicalSubscriber(input);
3273
3287
  return this.insert("subscriber_versions", "subscriber", document.subscriberId, document.version, document);
3274
3288
  }
3275
- /** Validates a dormant judge version; this deliberately does not require an enabled kind. */
3289
+ /**
3290
+ * Validates a dormant judge version; this deliberately does not require an enabled kind. Under B3 v1 and v2 a Laya
3291
+ * judge, or a rule naming one, is refused with the kind's own closed reason wherever this release or platform does
3292
+ * not offer Laya — with the placeholder lock, everywhere. Under B3 v3 it is kept as configuration, naming this
3293
+ * release's reviewed manifest and a template the worker runs (JudgeDocumentContract).
3294
+ */
3276
3295
  prepareJudge(input) {
3277
- return canonicalJudge(input);
3296
+ const document = canonicalJudge(input);
3297
+ if (this.#judgeContract === "b3-control-v3") {
3298
+ assertCreatableLayaJudge(document, this.#layaInputs);
3299
+ return document;
3300
+ }
3301
+ if (document.kind === JUDGE_KINDS[1]) assertLayaJudgeKindAvailable(this.#layaInputs);
3302
+ return document;
3303
+ }
3304
+ /** B3 v1 and v2: a rule naming a Laya judge exists only where this release and platform offer Laya (E2 Task 8). */
3305
+ #assertLayaRuleOffered(document) {
3306
+ if (document.agentic?.judge.kind === JUDGE_KINDS[1]) assertLayaJudgeKindAvailable(this.#layaInputs);
3278
3307
  }
3279
3308
  createJudge(input) {
3280
3309
  const document = this.prepareJudge(input);
@@ -3309,16 +3338,15 @@ var ImmutableVersions = class {
3309
3338
  return this.insert("judge_budget_versions", "judge-budget", document.budgetId, document.version, document);
3310
3339
  }
3311
3340
  prepareJudgeKindDocument(input) {
3312
- return canonicalJudgeKindEnablement(input);
3341
+ return canonicalJudgeKindEnablement(input, this.#layaInputs);
3313
3342
  }
3314
3343
  /** The sole digest-verifying loader for an immutable kind enablement document. */
3315
3344
  loadJudgeKind(kind, version) {
3316
- if (isJudgeKind(kind) && judgeKindDefinition(kind).availability === "unavailable") throw new EventDomainError("E2_REQUIRED", "laya is unavailable until E2");
3317
3345
  const row = this.#database.prepare("SELECT document, digest FROM judge_kind_versions WHERE kind = ? AND version = ? AND revoked_at IS NULL").get(kind, version);
3318
3346
  if (row === void 0) throw new EventDomainError("VERSION_DOCUMENT_INVALID", "the requested judge kind version does not exist");
3319
3347
  let enablement;
3320
3348
  try {
3321
- enablement = canonicalJudgeKindEnablement(JSON.parse(row.document));
3349
+ enablement = canonicalJudgeKindEnablement(JSON.parse(row.document), this.#layaInputs);
3322
3350
  } catch {
3323
3351
  throw new EventDomainError("VERSION_DOCUMENT_INVALID", "the stored judge kind document is invalid");
3324
3352
  }
@@ -3327,6 +3355,10 @@ var ImmutableVersions = class {
3327
3355
  }
3328
3356
  /** Loads one exact immutable kind enablement document, never inferring a default enabled state. */
3329
3357
  prepareJudgeKind(kind, version) {
3358
+ if (isJudgeKind(kind) && judgeKindDefinition(kind).availability === "unavailable") {
3359
+ if (kind !== JUDGE_KINDS[1] || judgeKindDefinition(kind).implementation === void 0) throw new EventDomainError("E2_REQUIRED", "the judge kind requires its next phase implementation");
3360
+ assertLayaJudgeKindAvailable(this.#layaInputs);
3361
+ }
3330
3362
  return {
3331
3363
  documentVersion: 1,
3332
3364
  kind: "judge-kind",
@@ -3350,7 +3382,10 @@ var ImmutableVersions = class {
3350
3382
  if (row === void 0) throw new EventDomainError("VERSION_DOCUMENT_INVALID", "the requested rule version does not exist");
3351
3383
  const rule = canonicalFullRuleDocument(JSON.parse(row.document));
3352
3384
  this.assertEmbeddedVersions(rule);
3353
- if (rule.agentic !== void 0) assertJudgeKindEnabled(rule.agentic.judge.kind, this.activeVersion("judge-kind", rule.agentic.judge.kind) !== null);
3385
+ if (rule.agentic !== void 0) {
3386
+ if (rule.agentic.judge.kind === JUDGE_KINDS[1]) assertLayaRuleJudgeOffered(this.#layaInputs);
3387
+ assertJudgeKindEnabled(rule.agentic.judge.kind, this.activeVersion("judge-kind", rule.agentic.judge.kind) !== null);
3388
+ }
3354
3389
  return normaliseActivationDocument({
3355
3390
  documentVersion: 1,
3356
3391
  kind: "rule",
@@ -7240,6 +7275,7 @@ function ruleAuthority(database, scope, judge) {
7240
7275
  function providerLimit(document, provider) {
7241
7276
  const found = document.providers.find((entry) => entry.provider === provider);
7242
7277
  if (found === void 0) refusal$2("PROVIDER_BUDGET_UNCONFIGURED", "the judge provider has no approved budget ceiling");
7278
+ if (provider === "laya" && found.concurrency > 1) refusal$2("PROVIDER_BUDGET_UNCONFIGURED", "the Laya provider budget must remain one-at-a-time");
7243
7279
  return found;
7244
7280
  }
7245
7281
  function usage(database, where, args, now) {
@@ -7536,7 +7572,7 @@ var JudgeReservationIoGate = class {
7536
7572
  return this.#firstGateError;
7537
7573
  }
7538
7574
  finalGate = (phase, assertBeforeTransition) => {
7539
- const firstPhysicalPhase = this.#judgeKind === "typesafe" ? "dns" : "tcp";
7575
+ const firstPhysicalPhase = this.#judgeKind === "typesafe" ? "dns" : this.#judgeKind === "laya" ? "write" : "tcp";
7540
7576
  if (!this.#entered) {
7541
7577
  if (phase !== firstPhysicalPhase) {
7542
7578
  assertBeforeTransition?.();
@@ -8660,10 +8696,20 @@ function recoverRevokedJudgeVersions(input) {
8660
8696
  }
8661
8697
  //#endregion
8662
8698
  //#region src/runtime/judge-purge.ts
8699
+ const purgeListeners = /* @__PURE__ */ new WeakMap();
8700
+ /** One owner composes one listener per database; the returned function removes exactly that registration. */
8701
+ function registerJudgeWorkPurgeListener(database, listener) {
8702
+ if (purgeListeners.has(database)) throw new Error("a judge work purge listener is already registered");
8703
+ purgeListeners.set(database, listener);
8704
+ return () => {
8705
+ if (purgeListeners.get(database) === listener) purgeListeners.delete(database);
8706
+ };
8707
+ }
8663
8708
  /**
8664
8709
  * The sole judge participant for a caller's already-open immediate transaction. It deliberately performs no
8665
8710
  * reservation transition itself: Task 5's idempotent recovery primitive releases pre-I/O work and terminalises
8666
- * post-I/O work exactly once.
8711
+ * post-I/O work exactly once. After each transition it tells the database's work listener, so every E purge caller
8712
+ * — they all reach judge work through this class — also stops the in-memory work that transition orphaned.
8667
8713
  */
8668
8714
  var JudgePurgeParticipant = class {
8669
8715
  #database;
@@ -8672,6 +8718,10 @@ var JudgePurgeParticipant = class {
8672
8718
  this.#database = input.database;
8673
8719
  this.#now = input.now ?? Date.now;
8674
8720
  }
8721
+ #purged(result) {
8722
+ purgeListeners.get(this.#database)?.onWorkPurged();
8723
+ return result;
8724
+ }
8675
8725
  all() {
8676
8726
  const result = recoverJudgeWork({
8677
8727
  database: this.#database,
@@ -8680,7 +8730,7 @@ var JudgePurgeParticipant = class {
8680
8730
  });
8681
8731
  purgeHeldDecisionsInTransaction(this.#database, { scope: "all" });
8682
8732
  this.#database.prepare("UPDATE decisions SET encrypted_record = NULL WHERE encrypted_record IS NOT NULL").run();
8683
- return result;
8733
+ return this.#purged(result);
8684
8734
  }
8685
8735
  kind(judgeKind) {
8686
8736
  const result = recoverJudgeWork({
@@ -8695,16 +8745,16 @@ var JudgePurgeParticipant = class {
8695
8745
  scope: "judge-kind",
8696
8746
  judgeKind
8697
8747
  });
8698
- return result;
8748
+ return this.#purged(result);
8699
8749
  }
8700
8750
  /** Judge removal is version-exact and delegates each durable identity to the single recovery transition. */
8701
8751
  judge(input) {
8702
- return recoverRevokedJudgeVersions({
8752
+ return this.#purged(recoverRevokedJudgeVersions({
8703
8753
  database: this.#database,
8704
8754
  now: this.#now(),
8705
8755
  judgeId: input.judgeId,
8706
8756
  versions: input.versions
8707
- });
8757
+ }));
8708
8758
  }
8709
8759
  account(input) {
8710
8760
  const result = recoverJudgeWork({
@@ -8720,7 +8770,7 @@ var JudgePurgeParticipant = class {
8720
8770
  scope: "source-account",
8721
8771
  ...input
8722
8772
  });
8723
- return result;
8773
+ return this.#purged(result);
8724
8774
  }
8725
8775
  event(input) {
8726
8776
  if (input.ruleId === void 0 !== (input.ruleVersion === void 0)) throw new Error("a rule-bound judge event purge needs both rule id and version");
@@ -8755,7 +8805,7 @@ var JudgePurgeParticipant = class {
8755
8805
  ruleId: input.ruleId,
8756
8806
  ruleVersion: input.ruleVersion
8757
8807
  });
8758
- return result;
8808
+ return this.#purged(result);
8759
8809
  }
8760
8810
  /**
8761
8811
  * Purge judge work for one retained projection without inferring its source from the replacement document. The
@@ -8777,10 +8827,10 @@ var JudgePurgeParticipant = class {
8777
8827
  released += result.released;
8778
8828
  settled += result.settled;
8779
8829
  }
8780
- return {
8830
+ return this.#purged({
8781
8831
  released,
8782
8832
  settled
8783
- };
8833
+ });
8784
8834
  }
8785
8835
  /** Enumerate the raw source/account identities first; each recovery call remains exact and account-scoped. */
8786
8836
  rule(input) {
@@ -8842,10 +8892,10 @@ var JudgePurgeParticipant = class {
8842
8892
  ruleId: input.ruleId,
8843
8893
  ruleVersion: row.rule_version
8844
8894
  });
8845
- return {
8895
+ return this.#purged({
8846
8896
  released,
8847
8897
  settled
8848
- };
8898
+ });
8849
8899
  }
8850
8900
  };
8851
8901
  //#endregion
@@ -11680,6 +11730,18 @@ const EVENT_MIGRATIONS = [
11680
11730
  END`,
11681
11731
  "UPDATE meta SET value = '22' WHERE key = 'schema_version'"
11682
11732
  ]
11733
+ },
11734
+ {
11735
+ version: 23,
11736
+ name: "event-laya-model-state-v1",
11737
+ statements: [`CREATE TABLE laya_models (
11738
+ manifest_id TEXT PRIMARY KEY CHECK (manifest_id <> ''),
11739
+ state TEXT NOT NULL CHECK (state IN ('installing', 'downloading', 'ready', 'failed', 'removed')),
11740
+ bytes_verified INTEGER NOT NULL CHECK (bytes_verified >= 0),
11741
+ verified_at INTEGER,
11742
+ failure_code TEXT,
11743
+ updated_at INTEGER NOT NULL CHECK (updated_at >= 0)
11744
+ ) STRICT`, "UPDATE meta SET value = '23' WHERE key = 'schema_version'"]
11683
11745
  }
11684
11746
  ];
11685
11747
  function initialiseLedger(database) {
@@ -11846,6 +11908,8 @@ var ActivationRuntime = class {
11846
11908
  #failpoint;
11847
11909
  #deadlineFailpoint;
11848
11910
  #assertObjectSecretReady;
11911
+ #layaInputs;
11912
+ #onJudgeKindEnabled;
11849
11913
  /** One owner process writes this state directory, so this in-memory queue is enough to serialise one approval. */
11850
11914
  #approvalOperations = /* @__PURE__ */ new Map();
11851
11915
  constructor(options) {
@@ -11873,10 +11937,15 @@ var ActivationRuntime = class {
11873
11937
  this.#failpoint = options.failpoint;
11874
11938
  this.#deadlineFailpoint = options.deadlineFailpoint;
11875
11939
  this.#assertObjectSecretReady = options.assertObjectSecretReady;
11940
+ this.#layaInputs = {
11941
+ ...options.layaAssetSource === void 0 ? {} : { layaAssetSource: options.layaAssetSource },
11942
+ ...options.layaPlatformProbe === void 0 ? {} : { layaPlatformProbe: options.layaPlatformProbe }
11943
+ };
11944
+ this.#onJudgeKindEnabled = options.onJudgeKindEnabled;
11876
11945
  }
11877
11946
  async prepareRule(input) {
11878
11947
  await this.assertNoCompletingMutation(input.ruleId);
11879
- const versions = new ImmutableVersions(this.#store.database);
11948
+ const versions = this.#versions();
11880
11949
  const document = versions.prepareRule(input.ruleId, input.version);
11881
11950
  await this.#assertDocumentSecretsReady(document.rule);
11882
11951
  this.#sourceRegistry.require(document.rule.source.channel);
@@ -11963,7 +12032,7 @@ var ActivationRuntime = class {
11963
12032
  ruleVersion: row.version
11964
12033
  }))
11965
12034
  };
11966
- const versions = new ImmutableVersions(this.#store.database);
12035
+ const versions = this.#versions();
11967
12036
  const liveBySource = /* @__PURE__ */ new Map();
11968
12037
  const points = [];
11969
12038
  for (const row of rows) {
@@ -11987,7 +12056,7 @@ var ActivationRuntime = class {
11987
12056
  /** Prepares the singleton budget solely from its immutable stored document. */
11988
12057
  async prepareJudgeBudget(input) {
11989
12058
  await this.assertNoCompletingMutation();
11990
- const versions = new ImmutableVersions(this.#store.database);
12059
+ const versions = this.#versions();
11991
12060
  const document = versions.prepareJudgeBudget(input.version);
11992
12061
  const active = versions.activeVersion("judge-budget", JUDGE_BUDGET_ID);
11993
12062
  if (active?.version === input.version) throw new CommsError("CONFIG", "the requested judge budget is already active");
@@ -12003,7 +12072,7 @@ var ActivationRuntime = class {
12003
12072
  /** Prepares a separately approved available judge-kind pointer; it performs no provider work. */
12004
12073
  async prepareJudgeKind(input) {
12005
12074
  const disableGeneration = this.#judgeKindDisableGeneration(input.kind);
12006
- const versions = new ImmutableVersions(this.#store.database);
12075
+ const versions = this.#versions();
12007
12076
  const document = versions.prepareJudgeKind(input.kind, input.version);
12008
12077
  await this.assertNoCompletingMutation();
12009
12078
  const active = versions.activeVersion("judge-kind", document.enablement.kind);
@@ -12267,7 +12336,7 @@ var ActivationRuntime = class {
12267
12336
  }
12268
12337
  async #prepare(document, points, effect, replacementOfVersion) {
12269
12338
  const intentId = this.#newIntentId();
12270
- const binding = disclosureBindingFor(intentId, document);
12339
+ const binding = disclosureBindingFor(intentId, document, this.#layaInputs);
12271
12340
  const now = this.#now();
12272
12341
  const plannedAccounts = [...new Map(points.map((point) => [`${point.source}\u0000${point.accountId}`, point])).values()];
12273
12342
  this.#store.immediate(() => {
@@ -12465,6 +12534,12 @@ var ActivationRuntime = class {
12465
12534
  if (document.kind === "rule") await this.#assertDocumentSecretsReady(document.rule);
12466
12535
  this.#finalise(current, document, preparedPoints, usedAt);
12467
12536
  this.#failpoint?.("after-move");
12537
+ if (document.kind === "judge-kind") try {
12538
+ this.#onJudgeKindEnabled?.({
12539
+ kind: document.enablement.kind,
12540
+ version: document.enablement.version
12541
+ });
12542
+ } catch {}
12468
12543
  return {
12469
12544
  intentId: current.id,
12470
12545
  status: "completed",
@@ -12506,7 +12581,7 @@ var ActivationRuntime = class {
12506
12581
  }
12507
12582
  }
12508
12583
  if (document.kind === "rule") {
12509
- const active = new ImmutableVersions(this.#store.database).activeVersion("rule", document.rule.ruleId);
12584
+ const active = this.#versions().activeVersion("rule", document.rule.ruleId);
12510
12585
  if (latest.replacement_of_version === null && active !== null) throw new CommsError("TRANSIENT", "the active rule pointer changed during activation", { details: { reason: "ACTIVATION_COMPLETING" } });
12511
12586
  if (latest.replacement_of_version !== null && (active === null || `${document.rule.ruleId}@${active.version}` !== latest.replacement_of_version)) {
12512
12587
  this.#cancel(intent.id, ACTIVATION_FAILURE.STALE_POINTER);
@@ -12594,11 +12669,11 @@ var ActivationRuntime = class {
12594
12669
  }
12595
12670
  #liveBinding(intent) {
12596
12671
  const document = this.#document(intent);
12597
- const binding = disclosureBindingFor(intent.id, document);
12598
- if (binding.digest !== intent.digest || activationDocumentDigest(document) !== intent.digest) throw new CommsError("APPROVAL_VOID", "the activation document changed after preparation");
12672
+ const binding = disclosureBindingFor(intent.id, document, this.#layaInputs);
12673
+ if (binding.digest !== intent.digest || activationDocumentDigest(document, this.#layaInputs) !== intent.digest) throw new CommsError("APPROVAL_VOID", "the activation document changed after preparation");
12599
12674
  this.#assertBoundRuleAuthority(document);
12600
12675
  if (document.kind === "rule") {
12601
- const active = new ImmutableVersions(this.#store.database).activeVersion("rule", document.rule.ruleId);
12676
+ const active = this.#versions().activeVersion("rule", document.rule.ruleId);
12602
12677
  if (active !== null && (intent.replacement_of_version === null || `${document.rule.ruleId}@${active.version}` !== intent.replacement_of_version)) throw new CommsError("APPROVAL_VOID", "the rule gained an active pointer after preparation");
12603
12678
  } else if (document.kind === "enable-all") {
12604
12679
  const current = this.#switch();
@@ -12624,19 +12699,22 @@ var ActivationRuntime = class {
12624
12699
  const expectedVersion = document.kind === "judge-budget" ? document.budget.version : document.enablement.version;
12625
12700
  if (pointer?.kind !== expectedKind || pointer.objectId !== expectedObjectId) throw new CommsError("APPROVAL_VOID", "the judge activation pointer expectation is invalid");
12626
12701
  if (document.kind === "judge-kind" && pointer.disableGeneration !== this.#judgeKindDisableGeneration(expectedObjectId)) assertJudgeKindEnabled(document.enablement.kind, false);
12627
- const versions = new ImmutableVersions(this.#store.database);
12702
+ const versions = this.#versions();
12628
12703
  if ((versions.activeVersion(pointer.kind, pointer.objectId)?.version ?? null) !== pointer.version) throw new CommsError("APPROVAL_VOID", "the judge activation pointer changed after preparation");
12629
12704
  const live = document.kind === "judge-budget" ? versions.prepareJudgeBudget(expectedVersion) : versions.prepareJudgeKind(expectedObjectId, expectedVersion);
12630
- if (canonicalJson(live) !== canonicalJson(document) || activationDocumentDigest(live) !== activationDocumentDigest(document)) throw new CommsError("APPROVAL_VOID", "the immutable judge activation document changed after preparation");
12705
+ if (canonicalJson(live) !== canonicalJson(document) || activationDocumentDigest(live, this.#layaInputs) !== activationDocumentDigest(document, this.#layaInputs)) throw new CommsError("APPROVAL_VOID", "the immutable judge activation document changed after preparation");
12631
12706
  }
12632
12707
  /** A disable advances this durable fence even when the kind has no active pointer yet. */
12708
+ #versions() {
12709
+ return new ImmutableVersions(this.#store.database, this.#layaInputs);
12710
+ }
12633
12711
  #judgeKindDisableGeneration(kind) {
12634
12712
  return this.#store.database.prepare("SELECT generation FROM judge_kind_disable_generations WHERE kind = ?").get(kind)?.generation ?? 0;
12635
12713
  }
12636
12714
  /** Re-read every exact bound rule, including its embedded judge revocation and kind pointer, at each authority gate. */
12637
12715
  #assertBoundRuleAuthority(document) {
12638
12716
  if (document.kind !== "rule" && document.kind !== "enable-all") return;
12639
- const versions = new ImmutableVersions(this.#store.database);
12717
+ const versions = this.#versions();
12640
12718
  if (document.kind === "rule") {
12641
12719
  const live = versions.prepareRule(document.rule.ruleId, document.rule.version);
12642
12720
  if (canonicalJson(live) !== canonicalJson(document)) throw new CommsError("APPROVAL_VOID", "the rule changed after preparation");
@@ -12647,7 +12725,7 @@ var ActivationRuntime = class {
12647
12725
  /** Preparation may await local stores; the exact pointer plan captured before those awaits must still be current. */
12648
12726
  #assertPreparationPlanLive(document, effect, replacementOfVersion) {
12649
12727
  if (document.kind === "rule") {
12650
- const active = new ImmutableVersions(this.#store.database).activeVersion("rule", document.rule.ruleId);
12728
+ const active = this.#versions().activeVersion("rule", document.rule.ruleId);
12651
12729
  if ((active === null ? null : `${document.rule.ruleId}@${active.version}`) !== (replacementOfVersion ?? null) || this.#switch().generation !== effect.switchGeneration) throw new CommsError("APPROVAL_VOID", "the rule activation pointer plan changed during preparation");
12652
12730
  return;
12653
12731
  }
@@ -13391,7 +13469,7 @@ function resolutionHealth(database, source, accountId, category) {
13391
13469
  }
13392
13470
  };
13393
13471
  }
13394
- function record(value) {
13472
+ function record$1(value) {
13395
13473
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
13396
13474
  }
13397
13475
  function retry(value, first, next, attempts) {
@@ -13405,13 +13483,13 @@ function retry(value, first, next, attempts) {
13405
13483
  }
13406
13484
  /** Extracts only retry timing/count fields and immediately discards the decrypted source continuation. */
13407
13485
  function retriesFromState(source, cursorScope, state, category) {
13408
- const decrypted = record(state);
13486
+ const decrypted = record$1(state);
13409
13487
  if (decrypted === null) return [];
13410
13488
  if (category === "metadata") {
13411
13489
  if (source !== "gmail" || cursorScope !== "mailbox") return [];
13412
- const states = record(decrypted.messageStates);
13490
+ const states = record$1(decrypted.messageStates);
13413
13491
  return states === null ? [] : Object.values(states).flatMap((message) => {
13414
- const values = record(message);
13492
+ const values = record$1(message);
13415
13493
  const pending = values === null ? null : retry(values, "firstFailedAt", "nextRetryAt", "attempts");
13416
13494
  return pending === null ? [] : [pending];
13417
13495
  });
@@ -13421,7 +13499,7 @@ function retriesFromState(source, cursorScope, state, category) {
13421
13499
  return pending === null ? [] : [pending];
13422
13500
  }
13423
13501
  if (source === "resend" && cursorScope === "received") {
13424
- const values = record(decrypted.retry);
13502
+ const values = record$1(decrypted.retry);
13425
13503
  const pending = values === null ? null : retry(values, "firstFailedAt", "nextRetryAt", "attempts");
13426
13504
  return pending === null ? [] : [pending];
13427
13505
  }
@@ -13700,6 +13778,23 @@ async function projectE9Doctor(input) {
13700
13778
  }
13701
13779
  });
13702
13780
  }
13781
+ /**
13782
+ * E2's successor beside the frozen doctor-v1 and doctor-v2 projections: the same v2 report, with no remaining judge
13783
+ * boundary, Laya's real kind state, and its content-free artifact entry from the one artifact manager.
13784
+ */
13785
+ async function projectE2Doctor(input) {
13786
+ const report = await projectE9Doctor(input);
13787
+ return assertDoctorReportV3({
13788
+ ...report,
13789
+ schemaVersion: "doctor-v3",
13790
+ judgeBoundary: [],
13791
+ judge: {
13792
+ ...report.judge,
13793
+ kinds: report.judge.kinds.map((entry) => entry.kind === "laya" ? input.laya.kindEntry() : entry),
13794
+ laya: await input.laya.doctor()
13795
+ }
13796
+ });
13797
+ }
13703
13798
  function whatsappHealth(database, live) {
13704
13799
  const applied = new Map(database.prepare("SELECT account_id, changed_at FROM whatsapp_visibility").all().map((row) => [row.account_id, row.changed_at]));
13705
13800
  const accountIds = new Set([...live].filter((identity) => identity.startsWith("whatsapp\0")).map((identity) => identity.slice(9)));
@@ -13757,7 +13852,8 @@ function unavailableE2() {
13757
13852
  //#region src/runtime/judge-kinds.ts
13758
13853
  function available(kind) {
13759
13854
  if (!JUDGE_KINDS.includes(kind)) throw new CommsError("BAD_DATA", "the judge kind is not recognised");
13760
- if (JUDGE_KIND_DEFINITIONS.find((entry) => entry.kind === kind)?.availability !== "available") throw new EventDomainError("E2_REQUIRED", "laya is unavailable until E2");
13855
+ const definition = JUDGE_KIND_DEFINITIONS.find((entry) => entry.kind === kind);
13856
+ if (definition?.availability !== "available" && definition?.implementation === void 0) throw new EventDomainError("E2_REQUIRED", "the judge kind is unavailable");
13761
13857
  return kind;
13762
13858
  }
13763
13859
  /** Task 4's dormant kind authority. Public operation registration remains Task 9's job. */
@@ -13765,10 +13861,21 @@ var JudgeKinds = class {
13765
13861
  #store;
13766
13862
  #now;
13767
13863
  #activations;
13864
+ #artifacts;
13768
13865
  constructor(input) {
13769
13866
  this.#store = input.store;
13770
13867
  this.#now = input.now ?? Date.now;
13771
13868
  this.#activations = input.activations;
13869
+ this.#artifacts = input.artifacts;
13870
+ }
13871
+ /**
13872
+ * The operation-facing disable. A kind with installed artifacts disables through their lifecycle, which wraps
13873
+ * exactly the transaction below; every other kind is that transaction alone.
13874
+ */
13875
+ async disableKind(kind) {
13876
+ const enabledKind = available(kind);
13877
+ if (this.#artifacts !== void 0 && this.#artifacts.kind === enabledKind) return this.#artifacts.disableKind(() => this.disable(enabledKind));
13878
+ return this.disable(enabledKind);
13772
13879
  }
13773
13880
  prepareEnable(input) {
13774
13881
  available(input.kind);
@@ -13812,6 +13919,12 @@ var B3OperatorControl = class {
13812
13919
  #activations;
13813
13920
  #judgeTest;
13814
13921
  #holdResolver;
13922
+ /**
13923
+ * E2's artifact manager. When the owner composes it, every Laya operation answers with the real model state and a
13924
+ * Laya judge is created and listed like any other (B3 control v3, which keeps v2's answers); without it this control
13925
+ * keeps B3 v1's frozen typed `E2_REQUIRED` answers and v1's judge documents, which have no Laya variant.
13926
+ */
13927
+ #laya;
13815
13928
  constructor(input) {
13816
13929
  this.#store = input.store;
13817
13930
  this.#ports = input.ports;
@@ -13819,9 +13932,10 @@ var B3OperatorControl = class {
13819
13932
  this.#activations = input.activations;
13820
13933
  this.#judgeTest = input.judgeTest;
13821
13934
  this.#holdResolver = input.holdResolver;
13935
+ this.#laya = input.laya;
13822
13936
  }
13823
13937
  async handle(request) {
13824
- const unavailable = request.operation === "judgeModelStatus" || request.operation === "removeJudgeModel" || (request.operation === "enableJudgeKind" || request.operation === "disableJudgeKind") && request.args.kind === "laya" ? judgeUnavailable(request.operation, request.args) : void 0;
13938
+ const unavailable = this.#laya === void 0 && (request.operation === "judgeModelStatus" || request.operation === "removeJudgeModel" || (request.operation === "enableJudgeKind" || request.operation === "disableJudgeKind") && request.args.kind === "laya") ? judgeUnavailable(request.operation, request.args) : void 0;
13825
13939
  if (unavailable !== void 0) return unavailable;
13826
13940
  switch (request.operation) {
13827
13941
  case "subscribersList": return this.subscribersList();
@@ -13833,7 +13947,7 @@ var B3OperatorControl = class {
13833
13947
  case "dropDelivery": return this.#ports.quiescer.withShared(async () => this.dropDelivery(requiredText$1(request.args.deliveryId, "delivery id")));
13834
13948
  case "judgesList": return this.judgesList();
13835
13949
  case "addJudge":
13836
- case "updateJudge": return this.#ports.quiescer.withShared(async () => this.#store.immediate(() => new ImmutableVersions(this.#store.database).createJudge(request.args.document)));
13950
+ case "updateJudge": return this.#ports.quiescer.withShared(async () => this.#store.immediate(() => this.#judgeVersions().createJudge(request.args.document)));
13837
13951
  case "removeJudge": return this.removeJudge(requiredText$1(request.args.judgeId, "judge id"));
13838
13952
  case "judgeTest":
13839
13953
  if (this.#judgeTest === void 0) throw new CommsError("CONFIG", "judge test is not composed for this operator control");
@@ -13846,6 +13960,8 @@ var B3OperatorControl = class {
13846
13960
  case "judgeKindsList": return this.judgeKindsList();
13847
13961
  case "enableJudgeKind": return this.enableJudgeKind(requiredText$1(request.args.kind, "judge kind"));
13848
13962
  case "disableJudgeKind": return this.disableJudgeKind(requiredText$1(request.args.kind, "judge kind"));
13963
+ case "judgeModelStatus": return this.requireLaya().status();
13964
+ case "removeJudgeModel": return this.requireLaya().remove();
13849
13965
  case "heldList": return this.heldList();
13850
13966
  case "decideHeld":
13851
13967
  if (this.#holdResolver === void 0) throw new CommsError("CONFIG", "held resolution has not been composed for this operator control");
@@ -13856,6 +13972,14 @@ var B3OperatorControl = class {
13856
13972
  default: throw new CommsError("USAGE", `the local B3 operator control does not implement ${request.operation}`);
13857
13973
  }
13858
13974
  }
13975
+ /** The judge documents this control answers: B3 v3's with E2's artifact manager, B3 v1's without it. */
13976
+ #judgeVersions() {
13977
+ return this.#laya === void 0 ? new ImmutableVersions(this.#store.database) : ImmutableVersions.forJudgeContract(this.#store.database, "b3-control-v3");
13978
+ }
13979
+ requireLaya() {
13980
+ if (this.#laya === void 0) throw new CommsError("CONFIG", "the Laya artifact manager is not composed for this operator control");
13981
+ return this.#laya;
13982
+ }
13859
13983
  requireActivations() {
13860
13984
  if (this.#activations === void 0) throw new CommsError("CONFIG", "judge activation is not composed for this operator control");
13861
13985
  return this.#activations;
@@ -13879,6 +14003,7 @@ var B3OperatorControl = class {
13879
14003
  }
13880
14004
  judgeKindsList() {
13881
14005
  return JUDGE_KINDS.map((kind) => {
14006
+ if (kind === "laya" && this.#laya !== void 0) return this.#laya.kindEntry();
13882
14007
  if (JUDGE_KIND_DEFINITIONS.find((entry) => entry.kind === kind)?.availability === "unavailable") return {
13883
14008
  kind: JUDGE_KIND_LAYA,
13884
14009
  state: "unavailable",
@@ -13899,6 +14024,18 @@ var B3OperatorControl = class {
13899
14024
  }
13900
14025
  async enableJudgeKind(kind) {
13901
14026
  const activations = this.requireActivations();
14027
+ if (kind === "laya" && this.#laya !== void 0) {
14028
+ const prepared = this.#laya.enablementVersion();
14029
+ if (!("version" in prepared)) return prepared;
14030
+ return new JudgeKinds({
14031
+ store: this.#store,
14032
+ now: this.#now,
14033
+ activations
14034
+ }).prepareEnable({
14035
+ kind: JUDGE_KIND_LAYA,
14036
+ version: prepared.version
14037
+ });
14038
+ }
13902
14039
  const available = availableJudgeKind(kind);
13903
14040
  const version = this.#store.immediate(() => {
13904
14041
  const latest = this.#store.database.prepare("SELECT max(version) AS version FROM judge_kind_versions WHERE kind = ? AND revoked_at IS NULL").get(available);
@@ -13929,8 +14066,9 @@ var B3OperatorControl = class {
13929
14066
  return new JudgeKinds({
13930
14067
  store: this.#store,
13931
14068
  now: this.#now,
13932
- activations
13933
- }).disable(kind);
14069
+ activations,
14070
+ artifacts: this.#laya
14071
+ }).disableKind(kind);
13934
14072
  }
13935
14073
  async removeJudge(judgeId) {
13936
14074
  return this.#store.immediate(() => {
@@ -13984,14 +14122,17 @@ var B3OperatorControl = class {
13984
14122
  return this.#store.database.prepare("SELECT judge_id, version, document, revoked_at FROM judge_versions ORDER BY judge_id, version").all().flatMap((row) => {
13985
14123
  try {
13986
14124
  const judge = canonicalJudge(JSON.parse(row.document));
13987
- return [{
14125
+ const entry = {
13988
14126
  judgeId: row.judge_id,
13989
14127
  version: row.version,
13990
14128
  kind: judge.kind,
13991
14129
  provider: judge.provider,
13992
14130
  model: judge.model,
13993
14131
  revokedAt: row.revoked_at
13994
- }];
14132
+ };
14133
+ if (this.#laya !== void 0) return [entry];
14134
+ if (judge.kind === "laya") return [];
14135
+ return [entry];
13995
14136
  } catch {
13996
14137
  return [];
13997
14138
  }
@@ -15454,19 +15595,27 @@ const systemAddressResolver = { async lookup(hostname) {
15454
15595
  })).map((answer) => answer.address);
15455
15596
  } };
15456
15597
  /** A literal host is its singleton answer; a hostname is resolved afresh by the injected resolver. */
15457
- async function resolveHost(hostname, resolver = systemAddressResolver) {
15598
+ async function resolveHost(hostname, resolver = systemAddressResolver, options = {}) {
15458
15599
  try {
15459
15600
  return [parseIpLiteral(hostname).address];
15460
15601
  } catch (error) {
15461
15602
  if (!(error instanceof AddressPolicyError)) throw error;
15462
15603
  }
15463
- const answers = await resolver.lookup(hostname);
15604
+ const answers = await resolver.lookup(hostname, options);
15464
15605
  if (answers.length === 0) throw new AddressPolicyError(`resolver returned no addresses for ${hostname}`);
15465
15606
  return answers;
15466
15607
  }
15467
15608
  //#endregion
15468
15609
  //#region src/network/pinned-connection.ts
15469
15610
  const TYPESAFE_GLOBAL_ONLY_ENDPOINT = "https://api.typesafe.ai/v1/systemone";
15611
+ var PinnedResponseError = class extends Error {
15612
+ code;
15613
+ constructor(code) {
15614
+ super(`pinned network response refused: ${code}`);
15615
+ this.name = "PinnedResponseError";
15616
+ this.code = code;
15617
+ }
15618
+ };
15470
15619
  function hostWithoutBrackets(hostname) {
15471
15620
  return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
15472
15621
  }
@@ -15497,6 +15646,9 @@ function assertPinnedUrl(url) {
15497
15646
  if (url.username !== "" || url.password !== "") throw new AddressPolicyError("network URL userinfo is refused");
15498
15647
  if (url.hash !== "") throw new AddressPolicyError("network URL fragments are refused");
15499
15648
  }
15649
+ function abortReason(signal) {
15650
+ return signal.reason instanceof Error ? signal.reason : new PinnedResponseError("NETWORK_ABORTED");
15651
+ }
15500
15652
  /** Resolves all answers, validates them all, and selects a literal only after the complete set passes. */
15501
15653
  async function preparePinnedConnection(options) {
15502
15654
  const url = typeof options.url === "string" ? new URL(options.url) : new URL(options.url.href);
@@ -15513,7 +15665,7 @@ async function preparePinnedConnection(options) {
15513
15665
  const literal = isLiteral(hostname);
15514
15666
  if (url.protocol === "http:" && (!literal || hostname !== "127.0.0.1" && hostname !== "::1")) throw new AddressPolicyError("HTTP requires literal 127.0.0.1 or ::1");
15515
15667
  if (url.protocol === "http:" && !globalOnly && !isSoleApprovedLiteral(options.approvedAddressSet, hostname)) throw new AddressPolicyError("HTTP requires its one exact approved literal loopback address");
15516
- const addresses = (await resolveHost(hostname, options.resolver ?? systemAddressResolver)).map((answer) => assertAddressAllowed(answer, options)).map((answer) => answer.address).sort((left, right) => left.localeCompare(right));
15668
+ const addresses = (await resolveHost(hostname, options.resolver ?? systemAddressResolver, { signal: options.signal })).map((answer) => assertAddressAllowed(answer, options)).map((answer) => answer.address).sort((left, right) => left.localeCompare(right));
15517
15669
  if (addresses.length === 0) throw new AddressPolicyError("resolver returned no vetted addresses");
15518
15670
  const [address] = addresses;
15519
15671
  if (address === void 0) throw new AddressPolicyError("resolver returned no selected address");
@@ -15545,36 +15697,160 @@ function requestBytesForPinnedConnection(connection, method, body = Buffer.alloc
15545
15697
  const supplied = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
15546
15698
  return Buffer.concat([Buffer.from(`${method} ${pathname} HTTP/1.1\r\nHost: ${connection.hostHeader}\r\nContent-Length: ${body.byteLength}\r\nConnection: close\r\n${supplied.join("\r\n")}\r\n\r\n`), Buffer.from(body)]);
15547
15699
  }
15700
+ /**
15701
+ * Generates a no-body raw GET for a fixed-purpose caller. The caller's header
15702
+ * policy is deliberately outside B2; B2 only guarantees that these exact raw
15703
+ * bytes go over a previously pinned connection.
15704
+ */
15705
+ function getRequestBytesForPinnedConnection(connection, headers) {
15706
+ const pathname = `${connection.url.pathname}${connection.url.search}` || "/";
15707
+ const supplied = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
15708
+ return Buffer.from(`GET ${pathname} HTTP/1.1\r\nHost: ${connection.hostHeader}\r\nConnection: close\r\n${supplied.join("\r\n")}\r\n\r\n`, "latin1");
15709
+ }
15710
+ /** Streams one bounded raw response without retaining its bytes in memory. */
15711
+ function streamPinnedResponseTo(socket, request, options) {
15712
+ const { maxBytes, onChunk, signal } = options;
15713
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) throw new PinnedResponseError("NETWORK_RESPONSE_TOO_LARGE");
15714
+ for (const deadline of [options.idleTimeoutMs, options.totalTimeoutMs]) if (deadline !== void 0 && (!Number.isSafeInteger(deadline) || deadline < 1)) throw new PinnedResponseError("NETWORK_TIMEOUT_INVALID");
15715
+ return new Promise((resolve, reject) => {
15716
+ let total = 0;
15717
+ let terminal;
15718
+ let ended = false;
15719
+ let pending = Promise.resolve();
15720
+ let idleTimer;
15721
+ let totalTimer;
15722
+ const onError = (error) => fail(error);
15723
+ const onAbort = () => fail(signal === void 0 ? new PinnedResponseError("NETWORK_ABORTED") : abortReason(signal));
15724
+ const clearTimers = () => {
15725
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
15726
+ if (totalTimer !== void 0) clearTimeout(totalTimer);
15727
+ };
15728
+ const resetIdleDeadline = () => {
15729
+ if (options.idleTimeoutMs === void 0) return;
15730
+ if (idleTimer !== void 0) clearTimeout(idleTimer);
15731
+ idleTimer = setTimeout(() => fail(new PinnedResponseError("NETWORK_IDLE_TIMEOUT")), options.idleTimeoutMs);
15732
+ };
15733
+ const cleanup = () => {
15734
+ clearTimers();
15735
+ signal?.removeEventListener("abort", onAbort);
15736
+ socket.off("error", onError);
15737
+ };
15738
+ const finish = () => {
15739
+ if (terminal !== void 0) return;
15740
+ terminal = null;
15741
+ cleanup();
15742
+ pending.then(() => resolve());
15743
+ };
15744
+ const fail = (error) => {
15745
+ if (terminal !== void 0) return;
15746
+ terminal = error;
15747
+ cleanup();
15748
+ socket.destroy();
15749
+ pending.then(() => reject(error));
15750
+ };
15751
+ socket.once("error", onError);
15752
+ if (signal?.aborted === true) {
15753
+ fail(new PinnedResponseError("NETWORK_ABORTED"));
15754
+ return;
15755
+ }
15756
+ signal?.addEventListener("abort", onAbort, { once: true });
15757
+ resetIdleDeadline();
15758
+ if (options.totalTimeoutMs !== void 0) totalTimer = setTimeout(() => fail(new PinnedResponseError("NETWORK_TOTAL_TIMEOUT")), options.totalTimeoutMs);
15759
+ socket.on("data", (chunk) => {
15760
+ if (terminal !== void 0) return;
15761
+ resetIdleDeadline();
15762
+ total += chunk.byteLength;
15763
+ if (total > maxBytes) {
15764
+ fail(new PinnedResponseError("NETWORK_RESPONSE_TOO_LARGE"));
15765
+ return;
15766
+ }
15767
+ socket.pause();
15768
+ pending = pending.then(() => onChunk(Buffer.from(chunk))).then(() => {
15769
+ if (terminal === void 0) socket.resume();
15770
+ if (ended) finish();
15771
+ }, (error) => fail(error instanceof Error ? error : /* @__PURE__ */ new Error("raw pinned response sink failed")));
15772
+ });
15773
+ socket.once("end", () => {
15774
+ ended = true;
15775
+ pending.then(() => finish());
15776
+ });
15777
+ socket.once("close", () => {
15778
+ if (terminal === void 0) fail(new PinnedResponseError("NETWORK_RESPONSE_INCOMPLETE"));
15779
+ });
15780
+ try {
15781
+ socket.write(request);
15782
+ } catch (error) {
15783
+ fail(error instanceof Error ? error : /* @__PURE__ */ new Error("raw pinned request write failed"));
15784
+ }
15785
+ });
15786
+ }
15548
15787
  /** Opens the selected literal TCP peer. Callers place their final authority fence immediately before this call. */
15549
15788
  function connectPinnedTcp(options) {
15550
15789
  return new Promise((resolve, reject) => {
15790
+ if (options.signal?.aborted === true) {
15791
+ reject(abortReason(options.signal));
15792
+ return;
15793
+ }
15551
15794
  const socket = net.connect({
15552
15795
  host: options.host,
15553
15796
  port: options.port
15554
15797
  });
15555
- socket.once("error", reject);
15556
15798
  installErrorSink(socket);
15557
- socket.once("connect", () => {
15558
- socket.off("error", reject);
15799
+ const cleanup = () => {
15800
+ options.signal?.removeEventListener("abort", onAbort);
15801
+ socket.off("error", onError);
15802
+ socket.off("connect", onConnect);
15803
+ };
15804
+ const fail = (error) => {
15805
+ cleanup();
15806
+ socket.destroy();
15807
+ reject(error);
15808
+ };
15809
+ const onError = (error) => fail(error);
15810
+ const onAbort = () => fail(options.signal === void 0 ? new PinnedResponseError("NETWORK_ABORTED") : abortReason(options.signal));
15811
+ const onConnect = () => {
15812
+ cleanup();
15559
15813
  resolve(socket);
15560
- });
15814
+ };
15815
+ socket.once("error", onError);
15816
+ socket.once("connect", onConnect);
15817
+ options.signal?.addEventListener("abort", onAbort, { once: true });
15561
15818
  });
15562
15819
  }
15563
15820
  /** Starts TLS only over the already-pinned TCP socket. Callers fence immediately before this ClientHello. */
15564
15821
  function connectPinnedTls(options) {
15565
15822
  return new Promise((resolve, reject) => {
15823
+ if (options.signal?.aborted === true) {
15824
+ options.socket.destroy();
15825
+ reject(abortReason(options.signal));
15826
+ return;
15827
+ }
15566
15828
  const socket = tls.connect({
15567
15829
  socket: options.socket,
15568
15830
  rejectUnauthorized: options.rejectUnauthorized,
15569
15831
  ...options.servername === void 0 ? {} : { servername: options.servername },
15570
15832
  checkServerIdentity: (_name, certificate) => tls.checkServerIdentity(options.certificateHost, certificate)
15571
15833
  });
15572
- socket.once("error", reject);
15573
15834
  installErrorSink(socket);
15574
- socket.once("secureConnect", () => {
15575
- socket.off("error", reject);
15835
+ const cleanup = () => {
15836
+ options.signal?.removeEventListener("abort", onAbort);
15837
+ socket.off("error", onError);
15838
+ socket.off("secureConnect", onSecureConnect);
15839
+ };
15840
+ const fail = (error) => {
15841
+ cleanup();
15842
+ socket.destroy();
15843
+ reject(error);
15844
+ };
15845
+ const onError = (error) => fail(error);
15846
+ const onAbort = () => fail(options.signal === void 0 ? new PinnedResponseError("NETWORK_ABORTED") : abortReason(options.signal));
15847
+ const onSecureConnect = () => {
15848
+ cleanup();
15576
15849
  resolve(socket);
15577
- });
15850
+ };
15851
+ socket.once("error", onError);
15852
+ socket.once("secureConnect", onSecureConnect);
15853
+ options.signal?.addEventListener("abort", onAbort, { once: true });
15578
15854
  });
15579
15855
  }
15580
15856
  //#endregion
@@ -15982,6 +16258,462 @@ var JudgeTransport = class {
15982
16258
  }
15983
16259
  }
15984
16260
  };
16261
+ /**
16262
+ * Each worker's owner-only working and proof directory is a fresh `mkdtemp` beneath `/tmp`, which is sticky and owned
16263
+ * by root as B1-G's socket-directory check accepts. Task 7's probe binds `<directory>/laya-boundary-XXXXXX/
16264
+ * boundary.sock`, 35 bytes beyond it, so this 27-byte directory keeps the socket at 62 bytes, inside macOS's 103 for
16265
+ * any home: core's data directory fits only while the home is at most 22 bytes.
16266
+ */
16267
+ const LAYA_WORKER_DIRECTORY_PARENT = "/tmp";
16268
+ const LAYA_WORKER_DIRECTORY_PREFIX = "agentcomms-laya-";
16269
+ const BINDING_FIELDS = [
16270
+ "nonce",
16271
+ "pid",
16272
+ "profile",
16273
+ "entry",
16274
+ "pipeGeneration"
16275
+ ];
16276
+ function sameSelection(current, started) {
16277
+ return current !== null && current.epoch === started.epoch && current.version === started.version && current.digest === started.digest && current.manifestId === started.manifestId;
16278
+ }
16279
+ function exactFrame(frame, keys) {
16280
+ const found = Object.keys(frame).sort();
16281
+ const expected = [...keys].sort();
16282
+ return found.length === expected.length && found.every((key, index) => key === expected[index]);
16283
+ }
16284
+ function bound(frame, binding) {
16285
+ return BINDING_FIELDS.every((field) => frame[field] === binding[field]);
16286
+ }
16287
+ /** The worker's reply, in its closed shapes only: a finite value in [0, 1], never clamped, and its token count. */
16288
+ function replyFrame(frame, binding, requestId) {
16289
+ if (!bound(frame, binding) || frame.requestId !== requestId) return null;
16290
+ if (frame.type === "answer") {
16291
+ if (!exactFrame(frame, [
16292
+ "type",
16293
+ ...BINDING_FIELDS,
16294
+ "requestId",
16295
+ "value",
16296
+ "inputTokens"
16297
+ ])) return null;
16298
+ const { value, inputTokens } = frame;
16299
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) return null;
16300
+ if (!Number.isSafeInteger(inputTokens) || inputTokens < 1) return null;
16301
+ if (inputTokens > 512) return null;
16302
+ return {
16303
+ kind: "answer",
16304
+ value,
16305
+ inputTokens
16306
+ };
16307
+ }
16308
+ if (frame.type !== "refused" || !exactFrame(frame, [
16309
+ "type",
16310
+ ...BINDING_FIELDS,
16311
+ "requestId",
16312
+ "code"
16313
+ ])) return null;
16314
+ if (frame.code === "token-limit") return {
16315
+ kind: "refused",
16316
+ code: "laya-token-limit",
16317
+ preIo: false
16318
+ };
16319
+ if (frame.code === "malformed") return {
16320
+ kind: "refused",
16321
+ code: "laya-malformed",
16322
+ preIo: false
16323
+ };
16324
+ if (frame.code === "template-blocked") return {
16325
+ kind: "refused",
16326
+ code: "laya-template-blocked",
16327
+ preIo: false
16328
+ };
16329
+ return null;
16330
+ }
16331
+ function loadFrame(frame, binding) {
16332
+ if (!bound(frame, binding)) return null;
16333
+ if (frame.type === "loaded" && exactFrame(frame, ["type", ...BINDING_FIELDS])) return "loaded";
16334
+ if (frame.type === "load-refused" && exactFrame(frame, [
16335
+ "type",
16336
+ ...BINDING_FIELDS,
16337
+ "code"
16338
+ ])) return "refused";
16339
+ return null;
16340
+ }
16341
+ function refusedBefore(code) {
16342
+ return { refused: {
16343
+ kind: "refused",
16344
+ code,
16345
+ preIo: true
16346
+ } };
16347
+ }
16348
+ /**
16349
+ * The one isolated Laya worker (E2-7, D11): started lazily, one request at a time, and killed on a 30-second
16350
+ * deadline, a crash, any frame outside the protocol, a purge of its in-flight reservation, a kind disable or update
16351
+ * tightening, the owner's stop, and after ten idle minutes. It holds no database, account, event, target or secret:
16352
+ * E's coordinator reserves, gates and settles, and this supervisor moves only one bounded request to the child and
16353
+ * its bounded answer back. A later call after any kill starts a fresh child, proved again from the beginning.
16354
+ */
16355
+ var LayaWorkerSupervisor = class {
16356
+ #artifacts;
16357
+ #startWorker;
16358
+ #requestDeadlineMs;
16359
+ #idleExitMs;
16360
+ #loadDeadlineMs;
16361
+ #detach;
16362
+ #queue = Promise.resolve();
16363
+ #worker;
16364
+ /** Bumped by every stop: a start or request that began before it never adopts or keeps a worker. */
16365
+ #generation = 0;
16366
+ #stopping = new AbortController();
16367
+ constructor(options) {
16368
+ this.#artifacts = options.artifacts;
16369
+ this.#startWorker = options.startWorker ?? ((start) => createLayaWorkerBoundary(start));
16370
+ this.#requestDeadlineMs = options.requestDeadlineMs ?? 3e4;
16371
+ this.#idleExitMs = options.idleExitMs ?? 6e5;
16372
+ this.#loadDeadlineMs = options.loadDeadlineMs ?? 12e4;
16373
+ this.#detach = options.artifacts.attachWorker(this);
16374
+ }
16375
+ availability() {
16376
+ return this.#artifacts.availability();
16377
+ }
16378
+ /**
16379
+ * Before E reserves anything: this release must offer Laya here, the enabled entry must be the judge's own manifest
16380
+ * and list the judge's template, and the request must fit one frame. The body is what E reserves against.
16381
+ */
16382
+ prepare(input) {
16383
+ const availability = this.#artifacts.availability();
16384
+ if (!availability.available) return {
16385
+ ok: false,
16386
+ code: availability.code
16387
+ };
16388
+ const selection = this.#artifacts.workerSelection();
16389
+ if (selection === null) return {
16390
+ ok: false,
16391
+ code: "laya-not-ready"
16392
+ };
16393
+ const templateId = input.judge.promptTemplate;
16394
+ if (input.judge.model !== selection.manifestId || !isLayaTemplateId(templateId) || !selection.promptTemplates.includes(templateId) || input.state.length === 0 || input.instructions.length === 0) return {
16395
+ ok: false,
16396
+ code: "laya-adapter-blocked"
16397
+ };
16398
+ const body = Buffer.from(JSON.stringify({
16399
+ templateId,
16400
+ instructions: input.instructions,
16401
+ state: input.state
16402
+ }));
16403
+ if (body.length > 61440) return {
16404
+ ok: false,
16405
+ code: "laya-request-too-large"
16406
+ };
16407
+ return {
16408
+ ok: true,
16409
+ request: {
16410
+ templateId,
16411
+ instructions: input.instructions,
16412
+ state: input.state,
16413
+ body
16414
+ }
16415
+ };
16416
+ }
16417
+ /**
16418
+ * One request at a time: the next one's frame is not written until this one has settled. Work is tracked by its
16419
+ * E reservation from the moment it is handed over, so a purge reaches it while it waits, too.
16420
+ */
16421
+ execute(request, hooks) {
16422
+ const tracked = this.#artifacts.trackWork(hooks.reservationId);
16423
+ const run = this.#queue.then(() => this.#execute(request, hooks, tracked.signal));
16424
+ this.#queue = run.then(() => void 0, () => void 0);
16425
+ return run.finally(() => tracked.release());
16426
+ }
16427
+ /** Kills the worker — a kind disable, an update tightening, the owner's stop — and fails whatever waited on it. */
16428
+ async stop() {
16429
+ this.#generation += 1;
16430
+ const stopping = this.#stopping;
16431
+ this.#stopping = new AbortController();
16432
+ stopping.abort();
16433
+ const worker = this.#worker;
16434
+ this.#worker = void 0;
16435
+ if (worker !== void 0) await this.#kill(worker, "laya-stopped");
16436
+ }
16437
+ /** The owner's final stop: also detaches from the manager. */
16438
+ async close() {
16439
+ await this.stop();
16440
+ this.#detach();
16441
+ }
16442
+ async #execute(request, hooks, signal) {
16443
+ const generation = this.#generation;
16444
+ const stopping = this.#stopping.signal;
16445
+ if (signal.aborted) return {
16446
+ kind: "refused",
16447
+ code: "laya-purged",
16448
+ preIo: true
16449
+ };
16450
+ const started = await this.#ensureWorker(generation, signal, stopping);
16451
+ if ("refused" in started) return started.refused;
16452
+ const worker = started.worker;
16453
+ if (signal.aborted) return {
16454
+ kind: "refused",
16455
+ code: "laya-purged",
16456
+ preIo: true
16457
+ };
16458
+ if (generation !== this.#generation || this.#worker !== worker) return {
16459
+ kind: "refused",
16460
+ code: "laya-stopped",
16461
+ preIo: true
16462
+ };
16463
+ if (!sameSelection(this.#artifacts.workerSelection(), worker.selection)) return {
16464
+ kind: "refused",
16465
+ code: "laya-authority-changed",
16466
+ preIo: true
16467
+ };
16468
+ const requestId = createWorkerRequestId();
16469
+ let bytes;
16470
+ try {
16471
+ bytes = encodeWorkerFrame({
16472
+ type: "request",
16473
+ ...worker.binding,
16474
+ requestId,
16475
+ templateId: request.templateId,
16476
+ instructions: request.instructions,
16477
+ state: request.state
16478
+ });
16479
+ } catch {
16480
+ return {
16481
+ kind: "refused",
16482
+ code: "laya-request-too-large",
16483
+ preIo: true
16484
+ };
16485
+ }
16486
+ this.#clearIdle(worker);
16487
+ const reply = this.#expect(worker, this.#requestDeadlineMs, signal, stopping, (frame) => replyFrame(frame, worker.binding, requestId));
16488
+ try {
16489
+ hooks.finalGate();
16490
+ } catch {
16491
+ reply.cancel();
16492
+ this.#armIdle(worker);
16493
+ return {
16494
+ kind: "refused",
16495
+ code: "laya-authority-changed",
16496
+ preIo: true
16497
+ };
16498
+ }
16499
+ worker.child.stdin.write(bytes);
16500
+ const result = await reply.settled;
16501
+ if ("gone" in result) {
16502
+ await this.#kill(worker, result.gone);
16503
+ return {
16504
+ kind: "refused",
16505
+ code: result.gone,
16506
+ preIo: false
16507
+ };
16508
+ }
16509
+ this.#armIdle(worker);
16510
+ return result.value;
16511
+ }
16512
+ async #ensureWorker(generation, signal, stopping) {
16513
+ const running = this.#worker;
16514
+ if (running !== void 0) return { worker: running };
16515
+ const availability = this.#artifacts.availability();
16516
+ if (!availability.available) return refusedBefore(availability.code);
16517
+ const selection = this.#artifacts.workerSelection();
16518
+ if (selection === null) return refusedBefore("laya-not-ready");
16519
+ const verified = await this.#artifacts.verifyForWorkerStart();
16520
+ if (!verified.ok) return refusedBefore(verified.failureCode ?? "laya-not-ready");
16521
+ if (generation !== this.#generation || stopping.aborted) return refusedBefore("laya-stopped");
16522
+ if (signal.aborted) return refusedBefore("laya-purged");
16523
+ if (!sameSelection(this.#artifacts.workerSelection(), selection)) return refusedBefore("laya-authority-changed");
16524
+ let directory;
16525
+ try {
16526
+ directory = await mkdtemp(join(LAYA_WORKER_DIRECTORY_PARENT, LAYA_WORKER_DIRECTORY_PREFIX));
16527
+ } catch {
16528
+ return refusedBefore("boundary-failed");
16529
+ }
16530
+ const proof = new AbortController();
16531
+ const abortProof = () => proof.abort();
16532
+ signal.addEventListener("abort", abortProof, { once: true });
16533
+ stopping.addEventListener("abort", abortProof, { once: true });
16534
+ if (signal.aborted || stopping.aborted) proof.abort();
16535
+ let boundary;
16536
+ try {
16537
+ boundary = await this.#startWorker({
16538
+ dataDirectory: directory,
16539
+ signal: proof.signal
16540
+ });
16541
+ } catch {
16542
+ boundary = {
16543
+ ok: false,
16544
+ code: "boundary-failed",
16545
+ loadSent: false
16546
+ };
16547
+ } finally {
16548
+ signal.removeEventListener("abort", abortProof);
16549
+ stopping.removeEventListener("abort", abortProof);
16550
+ }
16551
+ if (!boundary.ok) {
16552
+ await rm(directory, {
16553
+ recursive: true,
16554
+ force: true
16555
+ }).catch(() => void 0);
16556
+ if (proof.signal.aborted) return refusedBefore(signal.aborted ? "laya-purged" : "laya-stopped");
16557
+ if (boundary.code === "boundary-failed") this.#artifacts.recordWorkerFailure(selection, "boundary-failed");
16558
+ return refusedBefore(boundary.code);
16559
+ }
16560
+ const proven = boundary;
16561
+ const worker = {
16562
+ child: proven.child,
16563
+ binding: proven.binding,
16564
+ selection,
16565
+ directory,
16566
+ close: () => proven.close(),
16567
+ pending: void 0,
16568
+ idle: void 0,
16569
+ closing: void 0
16570
+ };
16571
+ this.#read(worker);
16572
+ if (generation !== this.#generation || stopping.aborted || signal.aborted) {
16573
+ await this.#kill(worker, "laya-stopped");
16574
+ return refusedBefore(signal.aborted ? "laya-purged" : "laya-stopped");
16575
+ }
16576
+ if (!sameSelection(this.#artifacts.workerSelection(), selection)) {
16577
+ await this.#kill(worker, "laya-authority-changed");
16578
+ return refusedBefore("laya-authority-changed");
16579
+ }
16580
+ const loaded = this.#expect(worker, this.#loadDeadlineMs, signal, stopping, (frame) => loadFrame(frame, worker.binding));
16581
+ try {
16582
+ await proven.sendLoad({
16583
+ runtimeRoot: verified.runtimeRoot,
16584
+ modelDir: verified.modelRoot
16585
+ });
16586
+ } catch {
16587
+ loaded.cancel();
16588
+ await this.#kill(worker, "laya-crashed");
16589
+ return refusedBefore("laya-crashed");
16590
+ }
16591
+ const outcome = await loaded.settled;
16592
+ if ("gone" in outcome || outcome.value !== "loaded") {
16593
+ const gone = "gone" in outcome ? outcome.gone : "runtime-failed";
16594
+ await this.#kill(worker, gone);
16595
+ if (gone === "laya-purged" || gone === "laya-stopped") return refusedBefore(gone);
16596
+ this.#artifacts.recordWorkerFailure(selection, "runtime-failed");
16597
+ return refusedBefore("runtime-failed");
16598
+ }
16599
+ if (generation !== this.#generation || stopping.aborted) {
16600
+ await this.#kill(worker, "laya-stopped");
16601
+ return refusedBefore("laya-stopped");
16602
+ }
16603
+ this.#worker = worker;
16604
+ return { worker };
16605
+ }
16606
+ /** Attaches the child's one stdout reader. A proven child's stdout is paused, and stays paused until resumed. */
16607
+ #read(worker) {
16608
+ const parser = new WorkerFrameParser();
16609
+ worker.child.stdout.on("data", (chunk) => {
16610
+ let frames;
16611
+ try {
16612
+ frames = parser.push(chunk);
16613
+ } catch {
16614
+ this.#kill(worker, "laya-malformed");
16615
+ return;
16616
+ }
16617
+ for (const frame of frames) {
16618
+ const pending = worker.pending;
16619
+ if (pending === void 0) {
16620
+ this.#kill(worker, "laya-malformed");
16621
+ return;
16622
+ }
16623
+ pending.frame(frame);
16624
+ }
16625
+ });
16626
+ worker.child.once("exit", () => void this.#kill(worker, "laya-crashed"));
16627
+ worker.child.stdout.resume();
16628
+ if (worker.child.exitCode !== null || worker.child.signalCode !== null) this.#kill(worker, "laya-crashed");
16629
+ }
16630
+ /**
16631
+ * Waits for exactly one expected frame within a deadline. Any other frame, the child's end, the deadline, a purge of
16632
+ * the waiting request or a stop settles it as gone; `cancel` withdraws it when nothing was written.
16633
+ */
16634
+ #expect(worker, deadlineMs, signal, stopping, accept) {
16635
+ let resolve = () => void 0;
16636
+ const settled = new Promise((done) => {
16637
+ resolve = done;
16638
+ });
16639
+ let done = false;
16640
+ const onPurge = () => finish({ gone: "laya-purged" });
16641
+ const onStop = () => finish({ gone: "laya-stopped" });
16642
+ const timer = setTimeout(() => finish({ gone: "laya-timeout" }), deadlineMs);
16643
+ const pending = {
16644
+ frame: (frame) => {
16645
+ const value = accept(frame);
16646
+ finish(value === null ? { gone: "laya-malformed" } : { value });
16647
+ },
16648
+ gone: (code) => finish({ gone: code })
16649
+ };
16650
+ function withdraw() {
16651
+ if (done) return false;
16652
+ done = true;
16653
+ clearTimeout(timer);
16654
+ signal.removeEventListener("abort", onPurge);
16655
+ stopping.removeEventListener("abort", onStop);
16656
+ if (worker.pending === pending) worker.pending = void 0;
16657
+ return true;
16658
+ }
16659
+ function finish(result) {
16660
+ if (withdraw()) resolve(result);
16661
+ }
16662
+ worker.pending = pending;
16663
+ signal.addEventListener("abort", onPurge, { once: true });
16664
+ stopping.addEventListener("abort", onStop, { once: true });
16665
+ if (signal.aborted) onPurge();
16666
+ if (stopping.aborted) onStop();
16667
+ if (worker.closing !== void 0) finish({ gone: "laya-crashed" });
16668
+ return {
16669
+ settled,
16670
+ cancel: () => void withdraw()
16671
+ };
16672
+ }
16673
+ #clearIdle(worker) {
16674
+ if (worker.idle !== void 0) clearTimeout(worker.idle);
16675
+ worker.idle = void 0;
16676
+ }
16677
+ #armIdle(worker) {
16678
+ this.#clearIdle(worker);
16679
+ worker.idle = setTimeout(() => {
16680
+ if (worker.pending === void 0) this.#kill(worker, "laya-stopped");
16681
+ }, this.#idleExitMs);
16682
+ }
16683
+ /**
16684
+ * SIGKILL, wait, and remove the worker's owner-only directory. Whatever was waiting learns why first, and the child's
16685
+ * reader is detached, so no frame is read from a killed worker.
16686
+ */
16687
+ #kill(worker, reason) {
16688
+ worker.closing ??= (async () => {
16689
+ if (this.#worker === worker) this.#worker = void 0;
16690
+ this.#clearIdle(worker);
16691
+ worker.child.stdout.removeAllListeners("data");
16692
+ const pending = worker.pending;
16693
+ worker.pending = void 0;
16694
+ pending?.gone(reason);
16695
+ await worker.close().catch(() => void 0);
16696
+ await rm(worker.directory, {
16697
+ recursive: true,
16698
+ force: true
16699
+ }).catch(() => void 0);
16700
+ })();
16701
+ return worker.closing;
16702
+ }
16703
+ };
16704
+ /** One owner composes one Laya executor per database, as it does E's purge listener; a test composes its own. */
16705
+ const executors = /* @__PURE__ */ new WeakMap();
16706
+ function registerLayaJudgeExecutor(database, executor) {
16707
+ if (executors.has(database)) throw new Error("a Laya judge executor is already registered");
16708
+ executors.set(database, executor);
16709
+ return () => {
16710
+ if (executors.get(database) === executor) executors.delete(database);
16711
+ };
16712
+ }
16713
+ /** The executor E's coordinator and judge test reach a Laya judge through; none means Laya cannot run here. */
16714
+ function layaJudgeExecutorFor(database) {
16715
+ return executors.get(database);
16716
+ }
15985
16717
  //#endregion
15986
16718
  //#region src/runtime/judge-test.ts
15987
16719
  const TEST_ACCOUNT_ID = "synthetic-judge-test";
@@ -16020,6 +16752,29 @@ function activeRuleAuthority(database, judgeId, version) {
16020
16752
  }
16021
16753
  throw new JudgeBudgetError("JUDGE_AUTHORITY_CHANGED", "no active rule grants this exact judge test");
16022
16754
  }
16755
+ /**
16756
+ * A live Laya judge version where this release or platform does not offer Laya: Laya's own closed reason (the
16757
+ * placeholder lock's `E2_EXTERNAL_REFERENCE_BLOCKED`, or the platform's or the sandbox's), read from the database's
16758
+ * own Laya inputs as a rule naming one is (E2 Task 10a). Undefined for any other judge, and wherever Laya is offered.
16759
+ */
16760
+ function unofferedLayaJudge(database, input) {
16761
+ const row = database.prepare("SELECT document FROM judge_versions WHERE judge_id = ? AND version = ? AND revoked_at IS NULL").get(input.judgeId, input.version);
16762
+ if (row === void 0) return void 0;
16763
+ let kind;
16764
+ try {
16765
+ kind = JSON.parse(row.document).kind;
16766
+ } catch {
16767
+ return;
16768
+ }
16769
+ if (kind !== "laya") return void 0;
16770
+ try {
16771
+ assertLayaJudgeKindAvailable(layaEnablementInputsFor(database));
16772
+ return;
16773
+ } catch (error) {
16774
+ if (error instanceof EventDomainError) return error.code;
16775
+ throw error;
16776
+ }
16777
+ }
16023
16778
  function switchGeneration(database) {
16024
16779
  return database.prepare("SELECT switch_generation FROM event_settings WHERE singleton = 1").get()?.switch_generation ?? 0;
16025
16780
  }
@@ -16052,6 +16807,13 @@ var JudgeTestControl = class {
16052
16807
  }
16053
16808
  async test(input) {
16054
16809
  if (typeof input.judgeId !== "string" || input.judgeId.length === 0 || !Number.isSafeInteger(input.version) || input.version < 1) throw new CommsError("USAGE", "judge test needs one exact judge id and positive version");
16810
+ const unoffered = unofferedLayaJudge(this.#store.database, input);
16811
+ if (unoffered !== void 0) return {
16812
+ kind: "refused",
16813
+ judgeId: input.judgeId,
16814
+ version: input.version,
16815
+ code: unoffered
16816
+ };
16055
16817
  let authority;
16056
16818
  try {
16057
16819
  authority = activeRuleAuthority(this.#store.database, input.judgeId, input.version);
@@ -16078,6 +16840,7 @@ var JudgeTestControl = class {
16078
16840
  handles: []
16079
16841
  }
16080
16842
  });
16843
+ if (authority.judge.kind === "laya") return this.#testLaya(input, authority, authority.judge);
16081
16844
  let hostedApiKey;
16082
16845
  if (authority.judge.kind === "typesafe") {
16083
16846
  const key = await this.#hostedApiKeyFor({
@@ -16111,43 +16874,9 @@ var JudgeTestControl = class {
16111
16874
  code: prepared.code
16112
16875
  };
16113
16876
  }
16114
- const reservationId = this.#newId();
16115
16877
  let reservation;
16116
16878
  try {
16117
- reservation = reserveJudgeTest({
16118
- store: this.#store,
16119
- id: reservationId,
16120
- workId: `judge-test:${reservationId}`,
16121
- testWorkId: `judge-test:${reservationId}`,
16122
- judge: authority.judge,
16123
- finalEnvelope: prepared.request,
16124
- switchGeneration: switchGeneration(this.#store.database),
16125
- now: this.#now(),
16126
- authority: {
16127
- ruleId: authority.ruleId,
16128
- ruleVersion: authority.ruleVersion,
16129
- ..."activationId" in authority ? {
16130
- activationId: authority.activationId,
16131
- approval: authority.challenge
16132
- } : {}
16133
- },
16134
- assertAuthority: (database) => {
16135
- if ("activationId" in authority) {
16136
- const current = assertPendingRuleJudgeTestAuthority({
16137
- database,
16138
- activationId: authority.activationId,
16139
- ruleId: authority.ruleId,
16140
- ruleVersion: authority.ruleVersion,
16141
- judgeId: input.judgeId,
16142
- judgeVersion: input.version
16143
- });
16144
- if (current.question !== authority.question || current.maxInputTokens !== authority.maxInputTokens || current.maxOutputTokens !== authority.maxOutputTokens) throw new JudgeBudgetError("JUDGE_AUTHORITY_CHANGED", "the pending rule authority changed before judge test admission");
16145
- return;
16146
- }
16147
- const current = activeRuleAuthority(database, input.judgeId, input.version);
16148
- if (current.ruleId !== authority.ruleId || current.ruleVersion !== authority.ruleVersion) throw new JudgeBudgetError("JUDGE_AUTHORITY_CHANGED", "the active rule authority changed before judge test admission");
16149
- }
16150
- });
16879
+ reservation = this.#reserve(input, authority, prepared.request);
16151
16880
  } catch (error) {
16152
16881
  return refusal(input.judgeId, input.version, error);
16153
16882
  }
@@ -16198,13 +16927,2183 @@ var JudgeTestControl = class {
16198
16927
  reservationId: reservation.id
16199
16928
  };
16200
16929
  }
16201
- };
16202
- //#endregion
16203
- //#region src/runtime/lifecycle.ts
16204
- var EventLifecycle = class {
16205
- store;
16206
- now;
16207
- constructor(store, now = Date.now) {
16930
+ /** The one synthetic-test admission: the active singleton's global ceilings and this judge version's test charge. */
16931
+ #reserve(input, authority, finalEnvelope) {
16932
+ const reservationId = this.#newId();
16933
+ return reserveJudgeTest({
16934
+ store: this.#store,
16935
+ id: reservationId,
16936
+ workId: `judge-test:${reservationId}`,
16937
+ testWorkId: `judge-test:${reservationId}`,
16938
+ judge: authority.judge,
16939
+ finalEnvelope,
16940
+ switchGeneration: switchGeneration(this.#store.database),
16941
+ now: this.#now(),
16942
+ authority: {
16943
+ ruleId: authority.ruleId,
16944
+ ruleVersion: authority.ruleVersion,
16945
+ ..."activationId" in authority ? {
16946
+ activationId: authority.activationId,
16947
+ approval: authority.challenge
16948
+ } : {}
16949
+ },
16950
+ assertAuthority: (database) => {
16951
+ if ("activationId" in authority) {
16952
+ const current = assertPendingRuleJudgeTestAuthority({
16953
+ database,
16954
+ activationId: authority.activationId,
16955
+ ruleId: authority.ruleId,
16956
+ ruleVersion: authority.ruleVersion,
16957
+ judgeId: input.judgeId,
16958
+ judgeVersion: input.version
16959
+ });
16960
+ if (current.question !== authority.question || current.maxInputTokens !== authority.maxInputTokens || current.maxOutputTokens !== authority.maxOutputTokens) throw new JudgeBudgetError("JUDGE_AUTHORITY_CHANGED", "the pending rule authority changed before judge test admission");
16961
+ return;
16962
+ }
16963
+ const current = activeRuleAuthority(database, input.judgeId, input.version);
16964
+ if (current.ruleId !== authority.ruleId || current.ruleVersion !== authority.ruleVersion) throw new JudgeBudgetError("JUDGE_AUTHORITY_CHANGED", "the active rule authority changed before judge test admission");
16965
+ }
16966
+ });
16967
+ }
16968
+ async #testLaya(input, authority, judge) {
16969
+ const executor = layaJudgeExecutorFor(this.#store.database);
16970
+ if (executor === void 0) return {
16971
+ kind: "refused",
16972
+ judgeId: input.judgeId,
16973
+ version: input.version,
16974
+ code: "ADAPTER_REFERENCE_BLOCKED"
16975
+ };
16976
+ const syntheticState = canonicalJson(TEST_CLOUD_EVENT);
16977
+ const laya = executor.prepare({
16978
+ judge,
16979
+ state: syntheticState,
16980
+ instructions: authority.question
16981
+ });
16982
+ if (!laya.ok) return {
16983
+ kind: "refused",
16984
+ judgeId: input.judgeId,
16985
+ version: input.version,
16986
+ code: laya.code
16987
+ };
16988
+ let reservation;
16989
+ try {
16990
+ reservation = this.#reserve(input, authority, laya.request.body);
16991
+ } catch (error) {
16992
+ return refusal(input.judgeId, input.version, error);
16993
+ }
16994
+ const ioGate = new JudgeReservationIoGate({
16995
+ store: this.#store,
16996
+ reservationId: reservation.id,
16997
+ judgeKind: judge.kind,
16998
+ now: this.#now
16999
+ });
17000
+ const result = await executor.execute(laya.request, {
17001
+ reservationId: reservation.id,
17002
+ finalGate: () => ioGate.finalGate("write")
17003
+ });
17004
+ if (result.kind === "answer") {
17005
+ settleJudgeWork({
17006
+ store: this.#store,
17007
+ reservationId: reservation.id,
17008
+ result: "completed",
17009
+ actualInputTokens: result.inputTokens,
17010
+ now: this.#now()
17011
+ });
17012
+ return {
17013
+ kind: "completed",
17014
+ judgeId: input.judgeId,
17015
+ version: input.version,
17016
+ value: result.value,
17017
+ valueLabel: "probability",
17018
+ reservationId: reservation.id
17019
+ };
17020
+ }
17021
+ finalizeJudgeTransportRefusal({
17022
+ store: this.#store,
17023
+ reservationId: reservation.id,
17024
+ result: result.code === "laya-timeout" ? "timeout" : "transport-failure",
17025
+ now: this.#now()
17026
+ });
17027
+ if (ioGate.firstGateError !== void 0) return refusal(input.judgeId, input.version, ioGate.firstGateError);
17028
+ if (result.preIo) return {
17029
+ kind: "refused",
17030
+ judgeId: input.judgeId,
17031
+ version: input.version,
17032
+ code: result.code
17033
+ };
17034
+ return {
17035
+ kind: "deferred",
17036
+ judgeId: input.judgeId,
17037
+ version: input.version,
17038
+ code: result.code,
17039
+ reservationId: reservation.id
17040
+ };
17041
+ }
17042
+ };
17043
+ //#endregion
17044
+ //#region src/runtime/laya/artifact-paths.ts
17045
+ var LayaArtifactPathError = class extends Error {
17046
+ code = "ARTIFACT_PATH";
17047
+ constructor() {
17048
+ super("Laya artifact path is outside the reviewed artifact tree");
17049
+ this.name = "LayaArtifactPathError";
17050
+ }
17051
+ };
17052
+ function safeSegment(value) {
17053
+ return /^[a-z0-9][a-z0-9._-]*$/i.test(value) && value !== "." && value !== "..";
17054
+ }
17055
+ function safeRelativePath(value) {
17056
+ return value.length > 0 && !value.includes("\\") && !value.includes("\0") && !/[\uD800-\uDFFF]/u.test(value) && value.normalize("NFC") === value && value.split("/").every(safeSegment);
17057
+ }
17058
+ function below(root, target) {
17059
+ const resolvedRoot = resolve(root);
17060
+ const resolvedTarget = resolve(target);
17061
+ if (!resolvedTarget.startsWith(`${resolvedRoot}${sep}`)) throw new LayaArtifactPathError();
17062
+ return resolvedTarget;
17063
+ }
17064
+ /** Derives owner-controlled locations only from the reviewed manifest identity and file list. */
17065
+ function layaModelArtifactPaths(dataDirectory, manifestId) {
17066
+ if (!safeSegment(manifestId)) throw new LayaArtifactPathError();
17067
+ const models = below(dataDirectory, join(dataDirectory, "models"));
17068
+ const partial = below(models, join(models, `${manifestId}.partial`));
17069
+ const complete = below(models, join(models, manifestId));
17070
+ return {
17071
+ models,
17072
+ partial,
17073
+ complete,
17074
+ file(file, state) {
17075
+ if (!safeRelativePath(file.path)) throw new LayaArtifactPathError();
17076
+ return below(state === "partial" ? partial : complete, join(state === "partial" ? partial : complete, file.path));
17077
+ }
17078
+ };
17079
+ }
17080
+ //#endregion
17081
+ //#region src/runtime/laya/fetcher.ts
17082
+ const MAX_REDIRECTS = 5;
17083
+ const MAX_RESPONSE_HEADER_BYTES = 16384;
17084
+ const DEFAULT_IDLE_TIMEOUT_MS = 3e4;
17085
+ const DEFAULT_TOTAL_TIMEOUT_MS = 18e5;
17086
+ const USER_AGENT = "agentcomms-events/0.15.2";
17087
+ const REDIRECT_STATUSES = /* @__PURE__ */ new Set([
17088
+ 301,
17089
+ 302,
17090
+ 303,
17091
+ 307,
17092
+ 308
17093
+ ]);
17094
+ var LayaFetchError = class extends Error {
17095
+ code;
17096
+ constructor(code) {
17097
+ super(`Laya artifact download refused: ${code}`);
17098
+ this.name = "LayaFetchError";
17099
+ this.code = code;
17100
+ }
17101
+ };
17102
+ function exactLowercaseHex(value, length) {
17103
+ return new RegExp(`^[a-f0-9]{${length}}$`).test(value);
17104
+ }
17105
+ function responseHeaderLimit(value) {
17106
+ const limit = value ?? MAX_RESPONSE_HEADER_BYTES;
17107
+ if (!Number.isSafeInteger(limit) || limit < 1) throw new LayaFetchError("RESPONSE_HEADERS");
17108
+ return limit;
17109
+ }
17110
+ function fetchDeadline(value, fallback) {
17111
+ const deadline = value ?? fallback;
17112
+ if (!Number.isSafeInteger(deadline) || deadline < 1) throw new LayaFetchError("HTTP_RESPONSE");
17113
+ return deadline;
17114
+ }
17115
+ function parseResponse(bytes, headerLimit) {
17116
+ const boundary = bytes.indexOf("\r\n\r\n", 0, "latin1");
17117
+ if (boundary < 0 || boundary + 4 > headerLimit) throw new LayaFetchError("RESPONSE_HEADERS");
17118
+ const lines = bytes.subarray(0, boundary).toString("latin1").split("\r\n");
17119
+ const status = /^HTTP\/1\.[01] ([0-9]{3}) [^\r\n]*$/u.exec(lines[0] ?? "");
17120
+ if (status?.[1] === void 0) throw new LayaFetchError("HTTP_RESPONSE");
17121
+ const headers = /* @__PURE__ */ new Map();
17122
+ for (const line of lines.slice(1)) {
17123
+ const separator = line.indexOf(":");
17124
+ if (separator < 1) throw new LayaFetchError("RESPONSE_HEADERS");
17125
+ const name = line.slice(0, separator).toLowerCase();
17126
+ const value = line.slice(separator + 1).trim();
17127
+ if (headers.has(name) || /[\r\n]/u.test(value)) throw new LayaFetchError("RESPONSE_HEADERS");
17128
+ headers.set(name, value);
17129
+ }
17130
+ return {
17131
+ status: Number(status[1]),
17132
+ headers,
17133
+ body: bytes.subarray(boundary + 4)
17134
+ };
17135
+ }
17136
+ function contentLength(response) {
17137
+ const value = response.headers.get("content-length");
17138
+ if (value === void 0) return null;
17139
+ if (!/^(?:0|[1-9]\d*)$/u.test(value)) throw new LayaFetchError("RESPONSE_HEADERS");
17140
+ const length = Number(value);
17141
+ if (!Number.isSafeInteger(length)) throw new LayaFetchError("RESPONSE_HEADERS");
17142
+ return length;
17143
+ }
17144
+ function exactSriSha512(value) {
17145
+ const matched = /^sha512-([A-Za-z0-9+/]{86}==)$/.exec(value);
17146
+ if (matched?.[1] === void 0) throw new LayaFetchError("RUNTIME_INTEGRITY");
17147
+ const decoded = Buffer.from(matched[1], "base64");
17148
+ if (decoded.length !== 64 || decoded.toString("base64") !== matched[1]) throw new LayaFetchError("RUNTIME_INTEGRITY");
17149
+ return decoded;
17150
+ }
17151
+ function isExactRegistryTarballUrl(source) {
17152
+ if (!/^(?:@[-a-z0-9]+\/)?[-a-z0-9]+$/iu.test(source.name)) return false;
17153
+ if (!/^[0-9A-Za-z][0-9A-Za-z.+-]*$/u.test(source.version)) return false;
17154
+ const basename = source.name.slice(source.name.lastIndexOf("/") + 1);
17155
+ return source.url === `https://registry.npmjs.org/${source.name}/-/${basename}-${source.version}.tgz`;
17156
+ }
17157
+ function throwIfAborted(signal) {
17158
+ if (!signal.aborted) return;
17159
+ if (signal.reason instanceof LayaFetchError) throw signal.reason;
17160
+ throw new LayaFetchError("ABORTED");
17161
+ }
17162
+ /** One request-wide lifetime: total time starts before the first DNS query and never resets on redirects. */
17163
+ var FetchDeadline = class {
17164
+ #controller = new AbortController();
17165
+ #expiresAt;
17166
+ #onCallerAbort;
17167
+ #timer;
17168
+ signal = this.#controller.signal;
17169
+ constructor(callerSignal, totalTimeoutMs) {
17170
+ this.#expiresAt = Date.now() + totalTimeoutMs;
17171
+ this.#onCallerAbort = () => this.#abort(new LayaFetchError("ABORTED"));
17172
+ if (callerSignal.aborted) this.#onCallerAbort();
17173
+ else callerSignal.addEventListener("abort", this.#onCallerAbort, { once: true });
17174
+ this.#timer = setTimeout(() => this.#abort(new LayaFetchError("TOTAL_TIMEOUT")), totalTimeoutMs);
17175
+ this.#callerSignal = callerSignal;
17176
+ }
17177
+ #callerSignal;
17178
+ remainingMs() {
17179
+ throwIfAborted(this.signal);
17180
+ return Math.max(1, this.#expiresAt - Date.now());
17181
+ }
17182
+ dispose() {
17183
+ clearTimeout(this.#timer);
17184
+ this.#callerSignal.removeEventListener("abort", this.#onCallerAbort);
17185
+ }
17186
+ #abort(reason) {
17187
+ if (!this.signal.aborted) this.#controller.abort(reason);
17188
+ }
17189
+ };
17190
+ function mapPinnedResponseError(error) {
17191
+ if (error instanceof PinnedResponseError) {
17192
+ if (error.code === "NETWORK_ABORTED") throw new LayaFetchError("ABORTED");
17193
+ if (error.code === "NETWORK_IDLE_TIMEOUT") throw new LayaFetchError("IDLE_TIMEOUT");
17194
+ if (error.code === "NETWORK_TOTAL_TIMEOUT") throw new LayaFetchError("TOTAL_TIMEOUT");
17195
+ }
17196
+ throw error;
17197
+ }
17198
+ function safeManifestPath(value) {
17199
+ return value.length > 0 && !value.includes("\\") && !value.includes("\0") && !/[\uD800-\uDFFF]/u.test(value) && value.normalize("NFC") === value && value.split("/").every((part) => /^[a-z0-9][a-z0-9._-]*$/iu.test(part) && part !== "." && part !== "..");
17200
+ }
17201
+ function modelUrl(manifest, file) {
17202
+ if (manifest.source.host !== "huggingface.co") throw new LayaFetchError("HTTP_RESPONSE");
17203
+ if (!/^[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*$/iu.test(manifest.source.repo)) throw new LayaFetchError("HTTP_RESPONSE");
17204
+ if (!exactLowercaseHex(manifest.source.revision, 40) || !safeManifestPath(file.path)) throw new LayaFetchError("HTTP_RESPONSE");
17205
+ return new URL(`https://${manifest.source.host}/${manifest.source.repo}/resolve/${manifest.source.revision}/${file.path}`);
17206
+ }
17207
+ /** Rejects every optional request header before any pinned DNS/TCP/TLS activity begins. */
17208
+ function assertLayaRequestHeaders(headers) {
17209
+ const allowed = /* @__PURE__ */ new Set(["range", "user-agent"]);
17210
+ const seen = /* @__PURE__ */ new Set();
17211
+ for (const [rawName, value] of Object.entries(headers)) {
17212
+ const name = rawName.toLowerCase();
17213
+ if (!allowed.has(name)) throw new LayaFetchError("REQUEST_HEADER");
17214
+ if (seen.has(name)) throw new LayaFetchError("REQUEST_HEADER");
17215
+ seen.add(name);
17216
+ if (name === "user-agent" && value !== USER_AGENT) throw new LayaFetchError("REQUEST_HEADER");
17217
+ if (name === "range" && !/^bytes=[1-9]\d*-$/.test(value)) throw new LayaFetchError("REQUEST_HEADER");
17218
+ }
17219
+ }
17220
+ function expectedContentRange(value, start, size, received) {
17221
+ if (value === void 0) throw new LayaFetchError("CONTENT_RANGE");
17222
+ const matched = /^bytes (\d+)-(\d+)\/(\d+)$/u.exec(value);
17223
+ if (matched === null) throw new LayaFetchError("CONTENT_RANGE");
17224
+ const [, returnedStart, returnedEnd, total] = matched;
17225
+ if (Number(returnedStart) !== start || Number(total) !== size || Number(returnedEnd) !== start + received - 1 || received < 1 || start + received > size) throw new LayaFetchError("CONTENT_RANGE");
17226
+ }
17227
+ /** Fixed-purpose raw artifact reader: it owns no proxy, agent, HTTP client, or loopback exception. */
17228
+ var LayaArtifactFetcher = class {
17229
+ manifest;
17230
+ #runtimePackages;
17231
+ #resolver;
17232
+ #tcpConnect;
17233
+ #tlsConnect;
17234
+ #headerLimit;
17235
+ #idleTimeoutMs;
17236
+ #totalTimeoutMs;
17237
+ constructor(options) {
17238
+ this.manifest = options.manifest;
17239
+ this.#runtimePackages = options.runtimePackages;
17240
+ this.#resolver = options.resolver;
17241
+ this.#tcpConnect = options.tcpConnect ?? connectPinnedTcp;
17242
+ this.#tlsConnect = options.tlsConnect ?? connectPinnedTls;
17243
+ this.#headerLimit = responseHeaderLimit(options.maxResponseHeaderBytes);
17244
+ this.#idleTimeoutMs = fetchDeadline(options.idleTimeoutMs, DEFAULT_IDLE_TIMEOUT_MS);
17245
+ this.#totalTimeoutMs = fetchDeadline(options.totalTimeoutMs, DEFAULT_TOTAL_TIMEOUT_MS);
17246
+ }
17247
+ async fetchRuntimeArchive(input, options) {
17248
+ const runtimePackage = this.#runtimePackages.find((candidate) => candidate.name === input.name && candidate.version === input.version);
17249
+ if (runtimePackage === void 0) throw new LayaFetchError("RUNTIME_ORIGIN");
17250
+ if (!isExactRegistryTarballUrl(runtimePackage)) throw new LayaFetchError("RUNTIME_ORIGIN");
17251
+ const expected = exactSriSha512(runtimePackage.integrity);
17252
+ const chunks = [];
17253
+ const result = await this.#get(new URL(runtimePackage.url), void 0, 536870912, async (chunk) => {
17254
+ chunks.push(Buffer.from(chunk));
17255
+ }, options.signal, false);
17256
+ const bytes = Buffer.concat(chunks);
17257
+ if (result.status !== 200 || contentLength(result) !== null && contentLength(result) !== bytes.length) throw new LayaFetchError("HTTP_RESPONSE");
17258
+ const actual = createHash("sha512").update(bytes).digest();
17259
+ if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) throw new LayaFetchError("RUNTIME_INTEGRITY");
17260
+ return bytes;
17261
+ }
17262
+ async fetchModelFile(file, resumeFrom, options) {
17263
+ const chunks = [];
17264
+ const result = await this.fetchModelFileTo(file, resumeFrom, async (chunk) => {
17265
+ chunks.push(Buffer.from(chunk));
17266
+ }, options);
17267
+ return {
17268
+ bytes: Buffer.concat(chunks),
17269
+ url: result.url,
17270
+ status: result.status,
17271
+ headers: result.headers
17272
+ };
17273
+ }
17274
+ async fetchModelFileTo(file, resumeFrom, onChunk, options) {
17275
+ if (!Number.isSafeInteger(file.size) || file.size < 1 || !exactLowercaseHex(file.sha256, 64)) throw new LayaFetchError("HTTP_RESPONSE");
17276
+ if (resumeFrom !== void 0 && (!Number.isSafeInteger(resumeFrom) || resumeFrom < 1 || resumeFrom >= file.size)) throw new LayaFetchError("CONTENT_RANGE");
17277
+ const result = await this.#get(modelUrl(this.manifest, file), resumeFrom, file.size, onChunk, options.signal, true);
17278
+ const declaredLength = contentLength(result);
17279
+ if (declaredLength !== null && declaredLength !== result.bytesReceived) throw new LayaFetchError("RESPONSE_BODY");
17280
+ if (resumeFrom === void 0) {
17281
+ if (result.status !== 200 || result.bytesReceived !== file.size) throw new LayaFetchError("HTTP_RESPONSE");
17282
+ } else {
17283
+ if (result.status !== 206) throw new LayaFetchError("CONTENT_RANGE");
17284
+ expectedContentRange(result.headers.get("content-range"), resumeFrom, file.size, result.bytesReceived);
17285
+ }
17286
+ return result;
17287
+ }
17288
+ async #globalApprovedAddresses(url, signal) {
17289
+ const hostname = url.hostname.startsWith("[") && url.hostname.endsWith("]") ? url.hostname.slice(1, -1) : url.hostname;
17290
+ let answers;
17291
+ try {
17292
+ answers = await resolveHost(hostname, this.#resolver, { signal });
17293
+ const vetted = answers.map((answer) => assertAddressAllowed(answer, { addressMode: "global-only" }));
17294
+ return [...new Set(vetted.map((answer) => answer.address))].sort((left, right) => left.localeCompare(right));
17295
+ } catch (error) {
17296
+ if (error instanceof AddressPolicyError) throw new LayaFetchError("ADDRESS");
17297
+ throw error;
17298
+ }
17299
+ }
17300
+ async #get(initial, resumeFrom, maximumBytes, onBody, signal, allowRedirects) {
17301
+ let url = new URL(initial.href);
17302
+ let redirects = 0;
17303
+ const deadline = new FetchDeadline(signal, this.#totalTimeoutMs);
17304
+ try {
17305
+ while (true) {
17306
+ throwIfAborted(deadline.signal);
17307
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.hash !== "") throw new LayaFetchError("REDIRECT");
17308
+ const approvedAddressSet = await this.#waitForPhase(() => this.#globalApprovedAddresses(url, deadline.signal), deadline);
17309
+ let connection;
17310
+ try {
17311
+ connection = await this.#waitForPhase(() => preparePinnedConnection({
17312
+ url,
17313
+ approvedAddressSet,
17314
+ approvedAddressSetDigest: addressSetDigest(approvedAddressSet),
17315
+ ...this.#resolver === void 0 ? {} : { resolver: this.#resolver },
17316
+ signal: deadline.signal
17317
+ }), deadline);
17318
+ } catch (error) {
17319
+ if (error instanceof AddressPolicyError) throw new LayaFetchError("ADDRESS");
17320
+ throw error;
17321
+ }
17322
+ const tcp = await this.#waitForPhase(() => this.#tcpConnect({
17323
+ host: connection.address,
17324
+ port: connection.port,
17325
+ signal: deadline.signal
17326
+ }), deadline, (late) => late.destroy());
17327
+ let socket;
17328
+ try {
17329
+ socket = await this.#waitForPhase(() => this.#tlsConnect({
17330
+ socket: tcp,
17331
+ ...connection.tls,
17332
+ signal: deadline.signal
17333
+ }), deadline, (late) => late.destroy());
17334
+ } catch (error) {
17335
+ tcp.destroy();
17336
+ throw error;
17337
+ }
17338
+ const headers = { "User-Agent": USER_AGENT };
17339
+ if (resumeFrom !== void 0) headers.Range = `bytes=${resumeFrom}-`;
17340
+ assertLayaRequestHeaders(headers);
17341
+ let header = Buffer.alloc(0);
17342
+ let response;
17343
+ let bytesReceived = 0;
17344
+ const hash = createHash("sha256");
17345
+ try {
17346
+ await streamPinnedResponseTo(socket, getRequestBytesForPinnedConnection(connection, headers), {
17347
+ maxBytes: maximumBytes + MAX_RESPONSE_HEADER_BYTES,
17348
+ signal: deadline.signal,
17349
+ idleTimeoutMs: this.#idleTimeoutMs,
17350
+ totalTimeoutMs: deadline.remainingMs(),
17351
+ onChunk: async (chunk) => {
17352
+ let body = chunk;
17353
+ if (response === void 0) {
17354
+ header = Buffer.concat([header, chunk]);
17355
+ const boundary = header.indexOf("\r\n\r\n", 0, "latin1");
17356
+ if (boundary < 0) {
17357
+ if (header.length > this.#headerLimit) throw new LayaFetchError("RESPONSE_HEADERS");
17358
+ return;
17359
+ }
17360
+ response = parseResponse(header.subarray(0, boundary + 4), this.#headerLimit);
17361
+ body = header.subarray(boundary + 4);
17362
+ header = Buffer.alloc(0);
17363
+ }
17364
+ if (response === void 0 || REDIRECT_STATUSES.has(response.status) || body.length === 0) return;
17365
+ bytesReceived += body.length;
17366
+ if (bytesReceived > maximumBytes) throw new LayaFetchError("RESPONSE_BODY");
17367
+ hash.update(body);
17368
+ await onBody(body, deadline.signal);
17369
+ }
17370
+ });
17371
+ } catch (error) {
17372
+ mapPinnedResponseError(error);
17373
+ }
17374
+ if (response === void 0) throw new LayaFetchError("RESPONSE_HEADERS");
17375
+ if (!REDIRECT_STATUSES.has(response.status)) return {
17376
+ ...response,
17377
+ bytesReceived,
17378
+ sha256: hash.digest("hex"),
17379
+ url: url.href
17380
+ };
17381
+ if (!allowRedirects) throw new LayaFetchError("REDIRECT");
17382
+ if (redirects >= MAX_REDIRECTS) throw new LayaFetchError("REDIRECT_LIMIT");
17383
+ const location = response.headers.get("location");
17384
+ if (location === void 0 || location.length === 0) throw new LayaFetchError("REDIRECT");
17385
+ try {
17386
+ url = new URL(location, url);
17387
+ } catch {
17388
+ throw new LayaFetchError("REDIRECT");
17389
+ }
17390
+ redirects += 1;
17391
+ }
17392
+ } finally {
17393
+ deadline.dispose();
17394
+ }
17395
+ }
17396
+ #waitForPhase(operation, deadline, destroyLate) {
17397
+ return new Promise((resolve, reject) => {
17398
+ let settled = false;
17399
+ const cleanup = () => deadline.signal.removeEventListener("abort", onAbort);
17400
+ const fail = (error) => {
17401
+ if (settled) return;
17402
+ settled = true;
17403
+ cleanup();
17404
+ reject(error);
17405
+ };
17406
+ const onAbort = () => {
17407
+ try {
17408
+ throwIfAborted(deadline.signal);
17409
+ } catch (error) {
17410
+ fail(error instanceof Error ? error : new LayaFetchError("ABORTED"));
17411
+ }
17412
+ };
17413
+ deadline.signal.addEventListener("abort", onAbort, { once: true });
17414
+ if (deadline.signal.aborted) {
17415
+ onAbort();
17416
+ return;
17417
+ }
17418
+ Promise.resolve().then(() => {
17419
+ throwIfAborted(deadline.signal);
17420
+ return operation();
17421
+ }).then((value) => {
17422
+ if (settled || deadline.signal.aborted) {
17423
+ destroyLate?.(value);
17424
+ onAbort();
17425
+ return;
17426
+ }
17427
+ settled = true;
17428
+ cleanup();
17429
+ resolve(value);
17430
+ }, (error) => fail(error instanceof Error ? error : /* @__PURE__ */ new Error("pinned download phase failed")));
17431
+ });
17432
+ }
17433
+ };
17434
+ //#endregion
17435
+ //#region src/runtime/laya/model-downloader.ts
17436
+ const OWNER_ONLY_DIRECTORY_MODE$3 = 448;
17437
+ const OWNER_ONLY_FILE_MODE$1 = 384;
17438
+ const MAX_FILE_ATTEMPTS = 5;
17439
+ var LayaModelDownloadError = class extends Error {
17440
+ code;
17441
+ constructor(code) {
17442
+ super(`Laya model download refused: ${code}`);
17443
+ this.name = "LayaModelDownloadError";
17444
+ this.code = code;
17445
+ }
17446
+ };
17447
+ async function ownerOnlyDirectory$2(path) {
17448
+ await mkdir(path, {
17449
+ recursive: true,
17450
+ mode: OWNER_ONLY_DIRECTORY_MODE$3
17451
+ });
17452
+ const info = await lstat(path);
17453
+ if (!info.isDirectory() || info.isSymbolicLink()) throw new LayaModelDownloadError("MODEL_PATH");
17454
+ await chmod(path, OWNER_ONLY_DIRECTORY_MODE$3);
17455
+ }
17456
+ async function missing(path) {
17457
+ try {
17458
+ await lstat(path);
17459
+ return false;
17460
+ } catch (error) {
17461
+ if (error.code === "ENOENT") return true;
17462
+ throw error;
17463
+ }
17464
+ }
17465
+ function abortIfNeeded(signal) {
17466
+ if (!signal.aborted) return;
17467
+ if (signal.reason instanceof LayaFetchError) throw downloadErrorForFetch(signal.reason);
17468
+ throw new LayaModelDownloadError("CANCELLED");
17469
+ }
17470
+ function awaitFenceWithSignal(wait, signal) {
17471
+ return new Promise((resolve, reject) => {
17472
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
17473
+ const onAbort = () => {
17474
+ cleanup();
17475
+ try {
17476
+ abortIfNeeded(signal);
17477
+ } catch (error) {
17478
+ reject(error);
17479
+ }
17480
+ };
17481
+ if (signal.aborted) {
17482
+ wait.catch(() => void 0);
17483
+ onAbort();
17484
+ return;
17485
+ }
17486
+ signal.addEventListener("abort", onAbort, { once: true });
17487
+ wait.then(() => {
17488
+ cleanup();
17489
+ resolve();
17490
+ }, (error) => {
17491
+ cleanup();
17492
+ reject(error);
17493
+ });
17494
+ });
17495
+ }
17496
+ function downloadErrorForFetch(error) {
17497
+ if (error.code === "ABORTED") return new LayaModelDownloadError("CANCELLED");
17498
+ if (error.code === "IDLE_TIMEOUT" || error.code === "TOTAL_TIMEOUT") return new LayaModelDownloadError("DEADLINE");
17499
+ return new LayaModelDownloadError("DOWNLOAD_FAILED");
17500
+ }
17501
+ /**
17502
+ * A file's size and SHA-256, streamed one read at a time as the tree verifier does: the model's data file is 1.69 GB,
17503
+ * which a whole-file read would hold in memory at once (and Node refuses to read a file of 2 GiB or more whole).
17504
+ */
17505
+ async function hashFile(path) {
17506
+ const hash = createHash("sha256");
17507
+ let size = 0;
17508
+ for await (const chunk of createReadStream(path)) {
17509
+ size += chunk.length;
17510
+ hash.update(chunk);
17511
+ }
17512
+ return {
17513
+ size,
17514
+ sha256: hash.digest("hex")
17515
+ };
17516
+ }
17517
+ async function partialSize(path) {
17518
+ try {
17519
+ const info = await lstat(path);
17520
+ if (!info.isFile() || info.isSymbolicLink()) throw new LayaModelDownloadError("MODEL_PATH");
17521
+ return info.size;
17522
+ } catch (error) {
17523
+ if (error.code === "ENOENT") return 0;
17524
+ throw error;
17525
+ }
17526
+ }
17527
+ async function verifyPartialFile(path, file, size) {
17528
+ const actual = await hashFile(path);
17529
+ if (actual.size !== size || size > file.size) throw new LayaModelDownloadError("DOWNLOAD_FAILED");
17530
+ if (size === file.size && actual.sha256 !== file.sha256) throw new LayaModelDownloadError("DOWNLOAD_FAILED");
17531
+ }
17532
+ async function availableBytes$1(path) {
17533
+ const stats = await statfs(path);
17534
+ return Number(stats.bavail) * Number(stats.bsize);
17535
+ }
17536
+ /** Downloads only the reviewed complete manifest tree, keeping every incomplete byte under one partial root. */
17537
+ var LayaModelDownloader = class {
17538
+ #dataDirectory;
17539
+ #manifest;
17540
+ #fetcher;
17541
+ #fence;
17542
+ constructor(options) {
17543
+ if (options.fence === void 0) throw new LayaModelDownloadError("LIFECYCLE_FENCE");
17544
+ this.#dataDirectory = options.dataDirectory;
17545
+ this.#manifest = options.manifest;
17546
+ this.#fetcher = options.fetcher;
17547
+ this.#fence = options.fence;
17548
+ }
17549
+ async download(options) {
17550
+ const paths = layaModelArtifactPaths(this.#dataDirectory, this.#manifest.manifestId);
17551
+ try {
17552
+ abortIfNeeded(options.signal);
17553
+ await this.#assertCurrent("start", options.signal);
17554
+ await ownerOnlyDirectory$2(paths.models);
17555
+ if (!await missing(paths.complete)) throw new LayaModelDownloadError("MODEL_EXISTS");
17556
+ await this.#assertCurrent("retry", options.signal);
17557
+ await ownerOnlyDirectory$2(paths.partial);
17558
+ const total = this.#manifest.files.reduce((sum, file) => sum + file.size, 0);
17559
+ const free = await (options.freeBytes ?? (() => availableBytes$1(paths.models)))();
17560
+ if (!Number.isSafeInteger(free) || free < Math.ceil(total * 1.1)) throw new LayaModelDownloadError("DISK_SPACE");
17561
+ let verified = 0;
17562
+ for (const file of this.#manifest.files) {
17563
+ abortIfNeeded(options.signal);
17564
+ await this.#assertCurrent("retry", options.signal);
17565
+ verified += await this.#downloadFile(paths.file(file, "partial"), file, options.signal);
17566
+ }
17567
+ abortIfNeeded(options.signal);
17568
+ if (!await missing(paths.complete)) throw new LayaModelDownloadError("MODEL_EXISTS");
17569
+ return this.#fence.withArtifactLock(async () => {
17570
+ abortIfNeeded(options.signal);
17571
+ if (!await missing(paths.complete)) throw new LayaModelDownloadError("MODEL_EXISTS");
17572
+ await this.#assertCurrent("promotion", options.signal);
17573
+ await rename(paths.partial, paths.complete);
17574
+ return {
17575
+ root: paths.complete,
17576
+ bytesVerified: verified
17577
+ };
17578
+ });
17579
+ } catch (error) {
17580
+ await rm(paths.partial, {
17581
+ recursive: true,
17582
+ force: true
17583
+ });
17584
+ if (error instanceof LayaModelDownloadError) throw error;
17585
+ if (error instanceof LayaFetchError) throw downloadErrorForFetch(error);
17586
+ throw error;
17587
+ }
17588
+ }
17589
+ async cleanupPartial() {
17590
+ const paths = layaModelArtifactPaths(this.#dataDirectory, this.#manifest.manifestId);
17591
+ await rm(paths.partial, {
17592
+ recursive: true,
17593
+ force: true
17594
+ });
17595
+ }
17596
+ async #assertCurrent(stage, signal) {
17597
+ await awaitFenceWithSignal(this.#fence.assertCurrent(stage, signal), signal);
17598
+ }
17599
+ async #downloadFile(path, file, signal) {
17600
+ await this.#assertCurrent("retry", signal);
17601
+ await ownerOnlyDirectory$2(dirname(path));
17602
+ for (let attempt = 0; attempt < MAX_FILE_ATTEMPTS; attempt += 1) {
17603
+ abortIfNeeded(signal);
17604
+ let current = await partialSize(path);
17605
+ if (current > 0) try {
17606
+ await verifyPartialFile(path, file, current);
17607
+ } catch {
17608
+ await rm(path, { force: true });
17609
+ current = 0;
17610
+ }
17611
+ try {
17612
+ let wrote = false;
17613
+ await this.#fetcher.fetchModelFileTo(file, current === 0 ? void 0 : current, async (chunk, fetchSignal) => {
17614
+ abortIfNeeded(fetchSignal);
17615
+ await this.#assertCurrent("retry", fetchSignal);
17616
+ abortIfNeeded(fetchSignal);
17617
+ if (current === 0 && !wrote) await writeFile(path, chunk, {
17618
+ mode: OWNER_ONLY_FILE_MODE$1,
17619
+ flag: "wx"
17620
+ });
17621
+ else await appendFile(path, chunk, { mode: OWNER_ONLY_FILE_MODE$1 });
17622
+ abortIfNeeded(fetchSignal);
17623
+ wrote = true;
17624
+ }, { signal });
17625
+ abortIfNeeded(signal);
17626
+ await this.#assertCurrent("retry", signal);
17627
+ await chmod(path, OWNER_ONLY_FILE_MODE$1);
17628
+ const final = await hashFile(path);
17629
+ await this.#assertCurrent("retry", signal);
17630
+ if (final.size !== file.size || final.sha256 !== file.sha256) throw new LayaModelDownloadError("DOWNLOAD_FAILED");
17631
+ return final.size;
17632
+ } catch (error) {
17633
+ await rm(path, { force: true });
17634
+ if (error instanceof LayaFetchError) {
17635
+ const terminal = downloadErrorForFetch(error);
17636
+ if (terminal.code === "CANCELLED" || terminal.code === "DEADLINE") throw terminal;
17637
+ }
17638
+ if (error instanceof LayaModelDownloadError && (error.code === "CANCELLED" || error.code === "DEADLINE")) throw error;
17639
+ if (attempt + 1 === MAX_FILE_ATTEMPTS) {
17640
+ if (error instanceof LayaModelDownloadError) throw error;
17641
+ if (error instanceof LayaFetchError) throw downloadErrorForFetch(error);
17642
+ throw new LayaModelDownloadError("DOWNLOAD_FAILED");
17643
+ }
17644
+ }
17645
+ }
17646
+ throw new LayaModelDownloadError("DOWNLOAD_FAILED");
17647
+ }
17648
+ };
17649
+ //#endregion
17650
+ //#region src/runtime/laya/tar.ts
17651
+ const BLOCK_SIZE = 512;
17652
+ const OWNER_ONLY_DIRECTORY_MODE$2 = 448;
17653
+ const OWNER_ONLY_FILE_MODE = 384;
17654
+ const EXECUTABLE_FILE_MODE = 448;
17655
+ var LayaTarError = class extends Error {
17656
+ code;
17657
+ constructor(code) {
17658
+ super(`Laya runtime archive refused: ${code}`);
17659
+ this.name = "LayaTarError";
17660
+ this.code = code;
17661
+ }
17662
+ };
17663
+ var ByteReader = class {
17664
+ #iterator;
17665
+ #buffer = Buffer.alloc(0);
17666
+ constructor(source) {
17667
+ this.#iterator = source[Symbol.asyncIterator]();
17668
+ }
17669
+ async readExactly(size) {
17670
+ while (this.#buffer.length < size) {
17671
+ const next = await this.#iterator.next();
17672
+ if (next.done) throw new LayaTarError("TAR_TRUNCATED");
17673
+ this.#buffer = Buffer.concat([this.#buffer, Buffer.from(next.value)]);
17674
+ }
17675
+ const value = this.#buffer.subarray(0, size);
17676
+ this.#buffer = this.#buffer.subarray(size);
17677
+ return value;
17678
+ }
17679
+ async ensureEndIsZero() {
17680
+ while (true) {
17681
+ const next = await this.#iterator.next();
17682
+ if (next.done) break;
17683
+ this.#buffer = Buffer.concat([this.#buffer, Buffer.from(next.value)]);
17684
+ }
17685
+ if (this.#buffer.some((byte) => byte !== 0)) throw new LayaTarError("TAR_HEADER");
17686
+ }
17687
+ };
17688
+ var TarBody = class {
17689
+ #reader;
17690
+ #remaining;
17691
+ constructor(reader, size) {
17692
+ this.#reader = reader;
17693
+ this.#remaining = size;
17694
+ }
17695
+ async *chunks() {
17696
+ while (this.#remaining > 0) {
17697
+ const count = Math.min(this.#remaining, 65536);
17698
+ const chunk = await this.#reader.readExactly(count);
17699
+ this.#remaining -= chunk.length;
17700
+ yield chunk;
17701
+ }
17702
+ }
17703
+ async drain() {
17704
+ for await (const _chunk of this.chunks());
17705
+ }
17706
+ };
17707
+ function isRegularFile(type) {
17708
+ return type === 0 || type === "0".charCodeAt(0);
17709
+ }
17710
+ function isSafeRelativePath(path) {
17711
+ return path.length > 0 && !path.startsWith("/") && !path.endsWith("/") && !path.includes("\\") && path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
17712
+ }
17713
+ function textField(bytes) {
17714
+ const firstNul = bytes.indexOf(0);
17715
+ const textBytes = firstNul === -1 ? bytes : bytes.subarray(0, firstNul);
17716
+ if (firstNul !== -1 && bytes.subarray(firstNul).some((byte) => byte !== 0)) throw new LayaTarError("TAR_PATH");
17717
+ try {
17718
+ return new TextDecoder("utf-8", { fatal: true }).decode(textBytes);
17719
+ } catch {
17720
+ throw new LayaTarError("TAR_PATH");
17721
+ }
17722
+ }
17723
+ function strictOctalField(bytes) {
17724
+ if (bytes.length < 2) throw new LayaTarError("TAR_HEADER");
17725
+ if (bytes.every((byte) => byte === 0)) return 0;
17726
+ const terminator = bytes[bytes.length - 1];
17727
+ if (terminator !== 0 && terminator !== 32) throw new LayaTarError("TAR_HEADER");
17728
+ let value = 0;
17729
+ let sawDigit = false;
17730
+ for (let index = 0; index < bytes.length - 1; index += 1) {
17731
+ const byte = bytes[index] ?? 0;
17732
+ if (byte === 32) {
17733
+ if (sawDigit) throw new LayaTarError("TAR_HEADER");
17734
+ continue;
17735
+ }
17736
+ if (byte < "0".charCodeAt(0) || byte > "7".charCodeAt(0)) throw new LayaTarError("TAR_HEADER");
17737
+ const digit = byte - "0".charCodeAt(0);
17738
+ if (value > Math.floor((Number.MAX_SAFE_INTEGER - digit) / 8)) throw new LayaTarError("TAR_HEADER");
17739
+ value = value * 8 + digit;
17740
+ sawDigit = true;
17741
+ }
17742
+ if (!sawDigit) throw new LayaTarError("TAR_HEADER");
17743
+ return value;
17744
+ }
17745
+ function checksumField(bytes) {
17746
+ if (bytes.length !== 8) throw new LayaTarError("TAR_CHECKSUM");
17747
+ const standardLayout = bytes[6] === 0 && bytes[7] === 32;
17748
+ const archiverLayout = bytes[7] === 0 || bytes[7] === 32;
17749
+ if (!standardLayout && !archiverLayout) throw new LayaTarError("TAR_CHECKSUM");
17750
+ return strictOctalField(bytes.subarray(0, standardLayout ? 7 : 8));
17751
+ }
17752
+ function numericHeaderFields(header) {
17753
+ const mode = strictOctalField(header.subarray(100, 108));
17754
+ strictOctalField(header.subarray(108, 116));
17755
+ strictOctalField(header.subarray(116, 124));
17756
+ const size = strictOctalField(header.subarray(124, 136));
17757
+ strictOctalField(header.subarray(136, 148));
17758
+ strictOctalField(header.subarray(329, 337));
17759
+ strictOctalField(header.subarray(337, 345));
17760
+ return {
17761
+ mode,
17762
+ size
17763
+ };
17764
+ }
17765
+ function headerChecksum(header) {
17766
+ let sum = 0;
17767
+ for (let index = 0; index < header.length; index += 1) sum += index >= 148 && index < 156 ? 32 : header[index] ?? 0;
17768
+ return sum;
17769
+ }
17770
+ function packagePathIdentity(path) {
17771
+ if (path !== path.normalize("NFC")) throw new LayaTarError("TAR_PATH");
17772
+ return path.toUpperCase().toLowerCase().normalize("NFC");
17773
+ }
17774
+ function recordPathIdentities(path, identities) {
17775
+ const parts = path.split("/");
17776
+ for (let index = 1; index <= parts.length; index += 1) {
17777
+ const candidate = parts.slice(0, index).join("/");
17778
+ const kind = index === parts.length ? "file" : "directory";
17779
+ const identity = packagePathIdentity(candidate);
17780
+ const previous = identities.get(identity);
17781
+ if (previous === void 0) {
17782
+ identities.set(identity, {
17783
+ path: candidate,
17784
+ kind
17785
+ });
17786
+ continue;
17787
+ }
17788
+ if (previous.path !== candidate || previous.kind !== kind) throw new LayaTarError("TAR_DUPLICATE");
17789
+ if (kind === "file") throw new LayaTarError("TAR_DUPLICATE");
17790
+ }
17791
+ }
17792
+ function parseEntry(header) {
17793
+ if (!header.subarray(257, 263).equals(Buffer.from("ustar\0", "ascii")) || !header.subarray(263, 265).equals(Buffer.from("00"))) throw new LayaTarError("TAR_HEADER");
17794
+ if (checksumField(header.subarray(148, 156)) !== headerChecksum(header)) throw new LayaTarError("TAR_CHECKSUM");
17795
+ const numeric = numericHeaderFields(header);
17796
+ if (!isRegularFile(header[156] ?? 0)) throw new LayaTarError("TAR_ENTRY_TYPE");
17797
+ const prefix = textField(header.subarray(345, 500));
17798
+ const name = textField(header.subarray(0, 100));
17799
+ const path = prefix.length === 0 ? name : `${prefix}/${name}`;
17800
+ if (path.includes("\\") || !isSafeRelativePath(path)) throw new LayaTarError("TAR_PATH");
17801
+ if (!path.startsWith("package/")) throw new LayaTarError("TAR_PATH");
17802
+ const relative = path.slice(8);
17803
+ packagePathIdentity(relative);
17804
+ return {
17805
+ path: relative,
17806
+ size: numeric.size,
17807
+ executable: (numeric.mode & 73) !== 0
17808
+ };
17809
+ }
17810
+ async function parseGzippedUstar(archive, onEntry) {
17811
+ const reader = new ByteReader(Readable.from([archive]).pipe(createGunzip()));
17812
+ const allPathIdentities = /* @__PURE__ */ new Map();
17813
+ while (true) {
17814
+ const header = await reader.readExactly(BLOCK_SIZE);
17815
+ if (header.every((byte) => byte === 0)) {
17816
+ if (!(await reader.readExactly(BLOCK_SIZE)).every((byte) => byte === 0)) throw new LayaTarError("TAR_HEADER");
17817
+ await reader.ensureEndIsZero();
17818
+ return;
17819
+ }
17820
+ const entry = parseEntry(header);
17821
+ recordPathIdentities(entry.path, allPathIdentities);
17822
+ const body = new TarBody(reader, entry.size);
17823
+ await onEntry(entry, body);
17824
+ await body.drain();
17825
+ const padding = (BLOCK_SIZE - entry.size % BLOCK_SIZE) % BLOCK_SIZE;
17826
+ if (padding > 0) await reader.readExactly(padding);
17827
+ }
17828
+ }
17829
+ function expectedFiles$1(files) {
17830
+ const result = /* @__PURE__ */ new Map();
17831
+ const identities = /* @__PURE__ */ new Map();
17832
+ for (const file of files) {
17833
+ if (!isSafeRelativePath(file.path) || !Number.isSafeInteger(file.size) || file.size < 0 || !/^[a-f0-9]{64}$/.test(file.sha256) || typeof file.executable !== "boolean") throw new LayaTarError("TAR_PATH");
17834
+ recordPathIdentities(file.path, identities);
17835
+ if (result.has(file.path)) throw new LayaTarError("TAR_DUPLICATE");
17836
+ result.set(file.path, file);
17837
+ }
17838
+ return result;
17839
+ }
17840
+ /** Validates a complete runtime tree listing before it is used to create or inspect a filesystem tree. */
17841
+ function validateRuntimeTreeFiles(files) {
17842
+ return expectedFiles$1(files);
17843
+ }
17844
+ async function archiveBytes(source) {
17845
+ const chunks = [];
17846
+ for await (const chunk of source) chunks.push(Buffer.from(chunk));
17847
+ return Buffer.concat(chunks);
17848
+ }
17849
+ async function validateExpectedPackageArchive(archive, expectedByPath) {
17850
+ const found = /* @__PURE__ */ new Set();
17851
+ await parseGzippedUstar(archive, async (entry, body) => {
17852
+ const expected = expectedByPath.get(entry.path);
17853
+ if (expected === void 0) return;
17854
+ if (entry.size !== expected.size) throw new LayaTarError("TAR_SIZE");
17855
+ if (entry.executable !== expected.executable) throw new LayaTarError("TAR_EXECUTABLE");
17856
+ const hash = createHash("sha256");
17857
+ for await (const chunk of body.chunks()) hash.update(chunk);
17858
+ if (hash.digest("hex") !== expected.sha256) throw new LayaTarError("TAR_HASH");
17859
+ found.add(entry.path);
17860
+ });
17861
+ for (const path of expectedByPath.keys()) if (!found.has(path)) throw new LayaTarError("TAR_MISSING");
17862
+ }
17863
+ async function makeOwnerOnlyDirectory(path) {
17864
+ await mkdir(path, {
17865
+ recursive: true,
17866
+ mode: OWNER_ONLY_DIRECTORY_MODE$2
17867
+ });
17868
+ await chmod(path, OWNER_ONLY_DIRECTORY_MODE$2);
17869
+ }
17870
+ async function writeAll(handle, bytes) {
17871
+ let offset = 0;
17872
+ while (offset < bytes.length) {
17873
+ const { bytesWritten } = await handle.write(bytes, offset, bytes.length - offset);
17874
+ if (bytesWritten === 0) throw new LayaTarError("TAR_TRUNCATED");
17875
+ offset += bytesWritten;
17876
+ }
17877
+ }
17878
+ async function assertAbsent$1(path) {
17879
+ try {
17880
+ await lstat(path);
17881
+ } catch (error) {
17882
+ if (error.code === "ENOENT") return;
17883
+ throw error;
17884
+ }
17885
+ throw new LayaTarError("TAR_PATH");
17886
+ }
17887
+ /** Extracts only expected files from a strict package archive into a caller-owned owner-only partial tree. */
17888
+ async function extractPackageArchive(options) {
17889
+ const expectedByPath = expectedFiles$1(options.files);
17890
+ const archive = await archiveBytes(options.source);
17891
+ await validateExpectedPackageArchive(archive, expectedByPath);
17892
+ const written = /* @__PURE__ */ new Set();
17893
+ await makeOwnerOnlyDirectory(dirname(options.destination));
17894
+ await assertAbsent$1(options.destination);
17895
+ const staging = await mkdtemp(join(dirname(options.destination), `.${basename(options.destination)}.staging-`));
17896
+ await chmod(staging, OWNER_ONLY_DIRECTORY_MODE$2);
17897
+ let promoted = false;
17898
+ try {
17899
+ await parseGzippedUstar(archive, async (entry, body) => {
17900
+ const expected = expectedByPath.get(entry.path);
17901
+ if (expected === void 0) return;
17902
+ if (entry.size !== expected.size) throw new LayaTarError("TAR_SIZE");
17903
+ if (entry.executable !== expected.executable) throw new LayaTarError("TAR_EXECUTABLE");
17904
+ const target = join(staging, entry.path);
17905
+ await makeOwnerOnlyDirectory(dirname(target));
17906
+ const handle = await open(target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, OWNER_ONLY_FILE_MODE);
17907
+ const hash = createHash("sha256");
17908
+ try {
17909
+ for await (const chunk of body.chunks()) {
17910
+ hash.update(chunk);
17911
+ await writeAll(handle, chunk);
17912
+ }
17913
+ } finally {
17914
+ await handle.close();
17915
+ }
17916
+ if (hash.digest("hex") !== expected.sha256) {
17917
+ await rm(target, { force: true });
17918
+ throw new LayaTarError("TAR_HASH");
17919
+ }
17920
+ await chmod(target, expected.executable ? EXECUTABLE_FILE_MODE : OWNER_ONLY_FILE_MODE);
17921
+ written.add(entry.path);
17922
+ });
17923
+ for (const path of expectedByPath.keys()) if (!written.has(path)) throw new LayaTarError("TAR_MISSING");
17924
+ await assertAbsent$1(options.destination);
17925
+ await rename(staging, options.destination);
17926
+ promoted = true;
17927
+ } finally {
17928
+ if (!promoted) await rm(staging, {
17929
+ recursive: true,
17930
+ force: true
17931
+ });
17932
+ }
17933
+ }
17934
+ //#endregion
17935
+ //#region src/runtime/laya/tree-verifier.ts
17936
+ function expectedFiles(files) {
17937
+ try {
17938
+ return new Map(validateRuntimeTreeFiles(files));
17939
+ } catch {
17940
+ return null;
17941
+ }
17942
+ }
17943
+ function expectedDirectories(expected) {
17944
+ const directories = /* @__PURE__ */ new Set();
17945
+ for (const path of expected.keys()) {
17946
+ const parts = path.split("/");
17947
+ for (let index = 1; index < parts.length; index += 1) directories.add(parts.slice(0, index).join("/"));
17948
+ }
17949
+ return directories;
17950
+ }
17951
+ async function sha256(path) {
17952
+ const hash = createHash("sha256");
17953
+ for await (const chunk of createReadStream(path)) hash.update(chunk);
17954
+ return hash.digest("hex");
17955
+ }
17956
+ async function verifyDirectory(root, relative, expected, directories) {
17957
+ let entries;
17958
+ try {
17959
+ entries = await readdir(join(root, relative), {
17960
+ encoding: "utf8",
17961
+ withFileTypes: true
17962
+ });
17963
+ } catch {
17964
+ return {
17965
+ ok: false,
17966
+ code: "TREE_IO"
17967
+ };
17968
+ }
17969
+ entries.sort((left, right) => Buffer.compare(Buffer.from(left.name), Buffer.from(right.name)));
17970
+ for (const entry of entries) {
17971
+ const relativePath = relative.length === 0 ? entry.name : `${relative}/${entry.name}`;
17972
+ let info;
17973
+ try {
17974
+ info = await lstat(join(root, relativePath));
17975
+ } catch {
17976
+ return {
17977
+ ok: false,
17978
+ code: "TREE_IO"
17979
+ };
17980
+ }
17981
+ if (info.isDirectory()) {
17982
+ if (!directories.has(relativePath)) return {
17983
+ ok: false,
17984
+ code: "TREE_EXTRA"
17985
+ };
17986
+ const child = await verifyDirectory(root, relativePath, expected, directories);
17987
+ if (!child.ok) return child;
17988
+ continue;
17989
+ }
17990
+ const expectedFile = expected.get(relativePath);
17991
+ if (expectedFile === void 0) return {
17992
+ ok: false,
17993
+ code: "TREE_EXTRA"
17994
+ };
17995
+ if (!info.isFile()) return {
17996
+ ok: false,
17997
+ code: "TREE_TYPE"
17998
+ };
17999
+ if (info.size !== expectedFile.size) return {
18000
+ ok: false,
18001
+ code: "TREE_SIZE"
18002
+ };
18003
+ if ((info.mode & 73) !== 0 !== expectedFile.executable) return {
18004
+ ok: false,
18005
+ code: "TREE_EXECUTABLE"
18006
+ };
18007
+ let digest;
18008
+ try {
18009
+ digest = await sha256(join(root, relativePath));
18010
+ } catch {
18011
+ return {
18012
+ ok: false,
18013
+ code: "TREE_IO"
18014
+ };
18015
+ }
18016
+ if (digest !== expectedFile.sha256) return {
18017
+ ok: false,
18018
+ code: "TREE_HASH"
18019
+ };
18020
+ expected.delete(relativePath);
18021
+ }
18022
+ return { ok: true };
18023
+ }
18024
+ /** Compares the complete selected runtime or model tree without following links or returning a path. */
18025
+ async function verifyRuntimeTree(options) {
18026
+ const expected = expectedFiles(options.files);
18027
+ if (expected === null) return {
18028
+ ok: false,
18029
+ code: "TREE_EXPECTED"
18030
+ };
18031
+ try {
18032
+ const root = await lstat(options.root);
18033
+ if (!root.isDirectory() || root.isSymbolicLink()) return {
18034
+ ok: false,
18035
+ code: "TREE_TYPE"
18036
+ };
18037
+ } catch {
18038
+ return {
18039
+ ok: false,
18040
+ code: "TREE_MISSING"
18041
+ };
18042
+ }
18043
+ const result = await verifyDirectory(options.root, "", expected, expectedDirectories(expected));
18044
+ if (!result.ok) return result;
18045
+ return expected.size === 0 ? { ok: true } : {
18046
+ ok: false,
18047
+ code: "TREE_MISSING"
18048
+ };
18049
+ }
18050
+ //#endregion
18051
+ //#region src/runtime/laya/runtime-installer.ts
18052
+ const OWNER_ONLY_DIRECTORY_MODE$1 = 448;
18053
+ var LayaRuntimeInstallError = class extends Error {
18054
+ code;
18055
+ constructor(code) {
18056
+ super(`Laya runtime installation refused: ${code}`);
18057
+ this.name = "LayaRuntimeInstallError";
18058
+ this.code = code;
18059
+ }
18060
+ };
18061
+ function isSafePackageName(packageName) {
18062
+ const parts = packageName.split("/");
18063
+ return (parts.length === 1 || parts.length === 2 && parts[0] !== void 0 && parts[0].startsWith("@")) && parts.every((part) => /^[a-z0-9][a-z0-9._-]*$/i.test(part.replace(/^@/, "")) && part !== "." && part !== "..");
18064
+ }
18065
+ async function ownerOnlyDirectory$1(path) {
18066
+ await mkdir(path, {
18067
+ recursive: true,
18068
+ mode: OWNER_ONLY_DIRECTORY_MODE$1
18069
+ });
18070
+ const info = await lstat(path);
18071
+ if (!info.isDirectory() || info.isSymbolicLink()) throw new LayaRuntimeInstallError("RUNTIME_PATH");
18072
+ await chmod(path, OWNER_ONLY_DIRECTORY_MODE$1);
18073
+ }
18074
+ async function assertAbsent(path) {
18075
+ try {
18076
+ await lstat(path);
18077
+ } catch (error) {
18078
+ if (error.code === "ENOENT") return;
18079
+ throw error;
18080
+ }
18081
+ throw new LayaRuntimeInstallError("RUNTIME_EXISTS");
18082
+ }
18083
+ function filesForPackage(packageName, files) {
18084
+ const prefix = `node_modules/${packageName}/`;
18085
+ const selected = [];
18086
+ for (const file of files) {
18087
+ if (file.packageName !== packageName || !file.path.startsWith(prefix)) {
18088
+ if (file.packageName === packageName) throw new LayaRuntimeInstallError("RUNTIME_PATH");
18089
+ continue;
18090
+ }
18091
+ selected.push({
18092
+ ...file,
18093
+ path: file.path.slice(prefix.length)
18094
+ });
18095
+ }
18096
+ return selected;
18097
+ }
18098
+ function validateInstallation(packages, files) {
18099
+ const packageNames = /* @__PURE__ */ new Set();
18100
+ for (const runtimePackage of packages) {
18101
+ if (!isSafePackageName(runtimePackage.packageName) || packageNames.has(runtimePackage.packageName)) throw new LayaRuntimeInstallError("RUNTIME_PATH");
18102
+ packageNames.add(runtimePackage.packageName);
18103
+ }
18104
+ for (const file of files) {
18105
+ if (!packageNames.has(file.packageName)) throw new LayaRuntimeInstallError("RUNTIME_PATH");
18106
+ if (!file.path.startsWith(`node_modules/${file.packageName}/`)) throw new LayaRuntimeInstallError("RUNTIME_PATH");
18107
+ }
18108
+ }
18109
+ /** Installs a complete selected lock tree into an owner-only partial directory and atomically promotes it once. */
18110
+ async function installRuntime(options) {
18111
+ if (!/^[a-f0-9]{64}$/.test(options.lockDigest)) throw new LayaRuntimeInstallError("RUNTIME_DIGEST");
18112
+ validateInstallation(options.packages, options.files);
18113
+ await ownerOnlyDirectory$1(options.runtimeDirectory);
18114
+ const root = join(options.runtimeDirectory, options.lockDigest);
18115
+ const partial = `${root}.partial`;
18116
+ await assertAbsent(root);
18117
+ await rm(partial, {
18118
+ recursive: true,
18119
+ force: true
18120
+ });
18121
+ await ownerOnlyDirectory$1(partial);
18122
+ try {
18123
+ for (const runtimePackage of options.packages) {
18124
+ const files = filesForPackage(runtimePackage.packageName, options.files);
18125
+ await extractPackageArchive({
18126
+ source: runtimePackage.source,
18127
+ destination: join(partial, "node_modules", runtimePackage.packageName),
18128
+ files
18129
+ });
18130
+ }
18131
+ if (!(await verifyRuntimeTree({
18132
+ root: partial,
18133
+ files: options.files
18134
+ })).ok) throw new LayaRuntimeInstallError("RUNTIME_TREE");
18135
+ await assertAbsent(root);
18136
+ await rename(partial, root);
18137
+ return { root };
18138
+ } catch (error) {
18139
+ await rm(partial, {
18140
+ recursive: true,
18141
+ force: true
18142
+ });
18143
+ if (error instanceof LayaTarError && error.code === "TAR_MISSING") throw new LayaRuntimeInstallError("RUNTIME_INCOMPLETE");
18144
+ throw error;
18145
+ }
18146
+ }
18147
+ //#endregion
18148
+ //#region src/runtime/laya/manager.ts
18149
+ const OWNER_ONLY_DIRECTORY_MODE = 448;
18150
+ /**
18151
+ * A content-free refusal a person or app can act on: its stable `details.reason` is `LAYA_ENABLED` (remove while the
18152
+ * kind is enabled) or `LAYA_NOT_READY` (a worker start before the kind is enabled); it names no path or content.
18153
+ */
18154
+ function layaRefusal(reason, message) {
18155
+ return new CommsError(reason === "LAYA_ENABLED" ? "USAGE" : "CONFIG", message, { details: { reason } });
18156
+ }
18157
+ /** A job or repair whose selection, pointer or epoch moved on: it stops without writing anything. */
18158
+ var ArtifactWorkStale = class extends Error {
18159
+ constructor() {
18160
+ super("the Laya artifact work no longer owns the active selection");
18161
+ this.name = "ArtifactWorkStale";
18162
+ }
18163
+ };
18164
+ /** A closed failure the job records for `doctor`, the app and `judge model status`. */
18165
+ var ArtifactFailure = class extends Error {
18166
+ failureCode;
18167
+ constructor(failureCode) {
18168
+ super(`Laya artifact work failed: ${failureCode}`);
18169
+ this.name = "ArtifactFailure";
18170
+ this.failureCode = failureCode;
18171
+ }
18172
+ };
18173
+ /** One FIFO lock: every artifact promotion, deletion, disable, removal and update tightening runs inside it. */
18174
+ var ArtifactMutex = class {
18175
+ #tail = Promise.resolve();
18176
+ run(work) {
18177
+ const result = this.#tail.then(work, work);
18178
+ this.#tail = result.then(() => void 0, () => void 0);
18179
+ return result;
18180
+ }
18181
+ };
18182
+ /** The selected platform's runtime files as the installer and verifier read them: rooted at `node_modules/`. */
18183
+ function runtimeFiles(selection) {
18184
+ return selection.runtimeLock.platform.files.map((file) => ({
18185
+ packageName: file.package,
18186
+ path: `node_modules/${file.package}/${file.path}`,
18187
+ size: file.size,
18188
+ sha256: file.sha256,
18189
+ executable: file.executable
18190
+ }));
18191
+ }
18192
+ function modelFiles(selection) {
18193
+ return selection.manifest.files.map((file) => ({
18194
+ ...file,
18195
+ executable: false
18196
+ }));
18197
+ }
18198
+ function bytesOf(files) {
18199
+ return files.reduce((sum, file) => sum + file.size, 0);
18200
+ }
18201
+ function totalBytes(selection) {
18202
+ return bytesOf(selection.manifest.files) + bytesOf(selection.runtimeLock.platform.files);
18203
+ }
18204
+ function record(value) {
18205
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
18206
+ }
18207
+ /**
18208
+ * D11's update rule over the complete approved entries: the enabled document must equal this release's document
18209
+ * byte for byte — its embedded manifest, its complete package list with every exact URL, version and SRI, and the
18210
+ * selected platform's complete file list, not merely a digest. A manifest difference is `model-changed`; any other
18211
+ * difference, a downgrade among them, is `runtime-changed`.
18212
+ */
18213
+ function layaUpdateReason(stored, release) {
18214
+ if (canonicalJson(stored) === canonicalJson(release)) return null;
18215
+ return canonicalJson(record(stored)?.manifest ?? null) !== canonicalJson(release.manifest) ? "model-changed" : "runtime-changed";
18216
+ }
18217
+ async function isDirectory(path) {
18218
+ try {
18219
+ const info = await lstat(path);
18220
+ return info.isDirectory() && !info.isSymbolicLink();
18221
+ } catch {
18222
+ return false;
18223
+ }
18224
+ }
18225
+ async function ownerOnlyDirectory(path) {
18226
+ await mkdir(path, {
18227
+ recursive: true,
18228
+ mode: OWNER_ONLY_DIRECTORY_MODE
18229
+ });
18230
+ }
18231
+ async function availableBytes(path) {
18232
+ const stats = await statfs(path);
18233
+ return Number(stats.bavail) * Number(stats.bsize);
18234
+ }
18235
+ /** The production fetcher, counting each streamed model byte for status only after the downloader's sink took it. */
18236
+ var ProgressReportingArtifactFetcher = class extends LayaArtifactFetcher {
18237
+ #progress;
18238
+ constructor(options, progress) {
18239
+ super(options);
18240
+ this.#progress = progress;
18241
+ }
18242
+ async fetchModelFileTo(file, resumeFrom, onChunk, options) {
18243
+ let received = resumeFrom ?? 0;
18244
+ this.#progress.set(file.path, received);
18245
+ return super.fetchModelFileTo(file, resumeFrom, async (chunk, signal) => {
18246
+ await onChunk(chunk, signal);
18247
+ received += chunk.length;
18248
+ this.#progress.set(file.path, received);
18249
+ }, options);
18250
+ }
18251
+ };
18252
+ /** How long a failed availability probe stands before the next request that needs it probes again. */
18253
+ const AVAILABILITY_RETRY_MS = 6e4;
18254
+ /**
18255
+ * The one serial owner of Laya's installation-wide artifacts. E stays the sole authority: the active Laya
18256
+ * judge-kind pointer starts every job, every post-await step rechecks that exact pointer, its embedded document and
18257
+ * this manager's epoch, and E's disable, purge and update tightenings stop the work they orphan. Only content-free
18258
+ * D8 model state is persisted; trees live under `runtime/<lockDigest>` and `models/<manifestId>` and nowhere else.
18259
+ */
18260
+ var LayaArtifactManager = class {
18261
+ kind = JUDGE_KIND_LAYA;
18262
+ #store;
18263
+ #root;
18264
+ #inputs;
18265
+ #reviewed;
18266
+ #network;
18267
+ #freeBytes;
18268
+ #audit;
18269
+ #now;
18270
+ #failpoint;
18271
+ #models;
18272
+ #mutex = new ArtifactMutex();
18273
+ #tracked = /* @__PURE__ */ new Map();
18274
+ #availability;
18275
+ #availabilityProbedAt = 0;
18276
+ /** Bumped by every disable, removal, update tightening and stop: a job captures it and stops once it moves. */
18277
+ #epoch = 0;
18278
+ #job;
18279
+ #stopped = false;
18280
+ #worker;
18281
+ constructor(options) {
18282
+ this.#store = options.store;
18283
+ this.#root = options.artifactRoot;
18284
+ this.#inputs = options.inputs ?? {};
18285
+ this.#network = options.network ?? {};
18286
+ this.#freeBytes = options.freeBytes;
18287
+ this.#audit = options.audit;
18288
+ this.#now = options.now ?? Date.now;
18289
+ this.#failpoint = options.failpoint;
18290
+ this.#models = new LayaModelRepository(options.store.database);
18291
+ }
18292
+ /**
18293
+ * The reviewed definition, parsed on first use: an owner whose Laya is never asked about never reads its assets,
18294
+ * so a start-up needs them only once a Laya pointer, status or operation does.
18295
+ */
18296
+ get #definition() {
18297
+ this.#reviewed ??= layaJudgeKindDefinitionFor(this.#inputs);
18298
+ return this.#reviewed;
18299
+ }
18300
+ /**
18301
+ * The content-free availability probe: a fact about this machine, never a document input. Only a success is kept for
18302
+ * the owner's life (E2 review round 1, B7); after a failure the next request that needs it probes again, at most once
18303
+ * a minute, so a sandbox that failed once is not taken as missing until the daemon restarts.
18304
+ */
18305
+ availability() {
18306
+ const now = this.#now();
18307
+ if (this.#availability === void 0 || !this.#availability.available && now - this.#availabilityProbedAt >= AVAILABILITY_RETRY_MS) {
18308
+ this.#availability = this.#definition.availability();
18309
+ this.#availabilityProbedAt = now;
18310
+ }
18311
+ return this.#availability;
18312
+ }
18313
+ /**
18314
+ * The judge-kind version a Laya enable prepares: the newest live version if it is exactly this release's reviewed
18315
+ * document, otherwise a new immutable version of it. Unavailable refuses before any version, intent or file exists.
18316
+ */
18317
+ enablementVersion() {
18318
+ const availability = this.availability();
18319
+ if (!availability.available) return unavailableResult(availability.code);
18320
+ return this.#store.immediate(() => {
18321
+ const latest = this.#store.database.prepare(`SELECT version, document FROM judge_kind_versions
18322
+ WHERE kind = ? AND revoked_at IS NULL ORDER BY version DESC LIMIT 1`).get(JUDGE_KIND_LAYA);
18323
+ if (latest !== void 0) {
18324
+ const release = this.#definition.preview({ version: latest.version });
18325
+ if (canonicalJson(JSON.parse(latest.document)) === canonicalJson(release)) return { version: latest.version };
18326
+ }
18327
+ const version = (this.#store.database.prepare("SELECT max(version) AS version FROM judge_kind_versions WHERE kind = ?").get("laya").version ?? 0) + 1;
18328
+ new ImmutableVersions(this.#store.database, this.#inputs).createJudgeKind(this.#definition.prepareEnablement({ version }));
18329
+ return { version };
18330
+ });
18331
+ }
18332
+ /** E's kind-list entry: the real pointer state while Laya is offered, its closed reason while it is not. */
18333
+ kindEntry() {
18334
+ const active = this.#activeEnablement();
18335
+ if (active !== null) return {
18336
+ kind: JUDGE_KIND_LAYA,
18337
+ state: "enabled",
18338
+ version: active.version
18339
+ };
18340
+ const availability = this.availability();
18341
+ if (!availability.available) return {
18342
+ kind: JUDGE_KIND_LAYA,
18343
+ state: "unavailable",
18344
+ code: availability.code
18345
+ };
18346
+ return {
18347
+ kind: JUDGE_KIND_LAYA,
18348
+ state: "disabled",
18349
+ version: null
18350
+ };
18351
+ }
18352
+ /**
18353
+ * Starts (or joins) the install for the active pointer. Before the enablement's claim there is no pointer, so this
18354
+ * does nothing: no request, row, directory or file. It never rejects; a failure becomes closed model state.
18355
+ */
18356
+ ensureInstalled() {
18357
+ try {
18358
+ return this.#ensureInstalled();
18359
+ } catch {
18360
+ return Promise.resolve();
18361
+ }
18362
+ }
18363
+ #ensureInstalled() {
18364
+ if (this.#stopped) return Promise.resolve();
18365
+ const active = this.#activeEnablement();
18366
+ if (active === null) return Promise.resolve();
18367
+ const running = this.#job;
18368
+ if (running !== void 0 && running.epoch === this.#epoch && running.version === active.version) return running.settled;
18369
+ const selection = this.#selectionFor(active);
18370
+ if (selection === null || !this.availability().available) return Promise.resolve();
18371
+ const job = {
18372
+ epoch: this.#epoch,
18373
+ version: active.version,
18374
+ digest: active.digest,
18375
+ selection,
18376
+ controller: new AbortController(),
18377
+ phase: "installing",
18378
+ runtimeBytes: 0,
18379
+ modelProgress: /* @__PURE__ */ new Map(),
18380
+ settled: Promise.resolve()
18381
+ };
18382
+ const previous = running?.settled ?? Promise.resolve();
18383
+ this.#job = job;
18384
+ job.settled = previous.then(() => this.#install(job)).finally(() => {
18385
+ if (this.#job === job) this.#job = void 0;
18386
+ });
18387
+ return job.settled;
18388
+ }
18389
+ /** Start-up's resume: an approved, unfinished install continues; a ready or failed model waits for its person. */
18390
+ async resume() {
18391
+ const active = this.#activeEnablement();
18392
+ if (active === null) return;
18393
+ const selection = this.#selectionFor(active);
18394
+ if (selection === null) return;
18395
+ const row = this.#models.get(selection.manifest.manifestId);
18396
+ if (row?.state === "ready" || row?.state === "failed") return;
18397
+ await this.ensureInstalled();
18398
+ }
18399
+ /**
18400
+ * E's immediate kind disable, wrapped: this manager's work is fenced and aborted first, E's whole disable
18401
+ * transaction runs under the artifact lock (so no promotion can land between its last recheck and its rename), and
18402
+ * then the partial and staging trees the disable orphaned are deleted. Verified trees survive.
18403
+ */
18404
+ async disableKind(disable) {
18405
+ const job = this.#fence();
18406
+ const result = await this.#mutex.run(async () => {
18407
+ const disabled = disable();
18408
+ await this.#removeUnverified();
18409
+ return disabled;
18410
+ });
18411
+ await job?.settled.catch(() => void 0);
18412
+ await this.#worker?.stop();
18413
+ return result;
18414
+ }
18415
+ /**
18416
+ * D11's start-up update rule. While Laya is enabled, its approved document is compared with this release's; any
18417
+ * difference applies E's kind-disable tightening with `model-changed` or `runtime-changed`, keeps verified trees,
18418
+ * and never re-approves. The tightening rechecks that the compared pointer is still the active one.
18419
+ */
18420
+ async reconcileRelease(disable) {
18421
+ const compared = this.#activeEnablement();
18422
+ if (compared === null) return null;
18423
+ let release;
18424
+ try {
18425
+ release = this.#definition.preview({ version: compared.version });
18426
+ } catch {
18427
+ release = null;
18428
+ }
18429
+ const reason = release === null ? "runtime-changed" : layaUpdateReason(compared.document, release);
18430
+ if (reason === null) return null;
18431
+ const job = this.#fence();
18432
+ const applied = await this.#locked("update-disable", async () => {
18433
+ const live = this.#activeEnablement();
18434
+ if (live === null || live.version !== compared.version || live.digest !== compared.digest) return false;
18435
+ disable();
18436
+ await this.#removeUnverified();
18437
+ return true;
18438
+ });
18439
+ await job?.settled.catch(() => void 0);
18440
+ if (!applied) return null;
18441
+ await this.#worker?.stop();
18442
+ await this.#audit?.append({
18443
+ inboxId: "",
18444
+ operation: "events.disableJudgeKind",
18445
+ outcome: "ok",
18446
+ reason,
18447
+ ids: {
18448
+ kind: JUDGE_KIND_LAYA,
18449
+ version: String(compared.version)
18450
+ },
18451
+ surface: "daemon",
18452
+ origin: "daemon"
18453
+ });
18454
+ return reason;
18455
+ }
18456
+ /**
18457
+ * `judge model remove` (D11): only while Laya is disabled, deletes every verified runtime and model tree under the
18458
+ * Laya root — an earlier release's first, then this release's own with its unverified copies. Another selection's
18459
+ * partial or staging tree is a disable's to delete, never a removal's. Each asynchronous deletion is followed by a
18460
+ * recheck that Laya is still disabled; an enablement claimed meanwhile stops the removal and owns what remains.
18461
+ */
18462
+ async remove() {
18463
+ await this.#mutex.run(async () => {
18464
+ if (this.#activeEnablement() !== null) throw layaRefusal("LAYA_ENABLED", "disable the Laya judge kind before removing its model");
18465
+ if (!await this.#removeEarlierVerifiedTrees()) return;
18466
+ const manifestId = this.#manifestId();
18467
+ const recorded = manifestId === null ? null : this.#models.get(manifestId);
18468
+ if (recorded === null || recorded.state === "removed") return;
18469
+ const release = this.#releaseSelection();
18470
+ if (release !== null) {
18471
+ const runtimeRoot = this.#runtimeRoot(release);
18472
+ await rm(runtimeRoot, {
18473
+ recursive: true,
18474
+ force: true
18475
+ });
18476
+ await rm(`${runtimeRoot}.staging`, {
18477
+ recursive: true,
18478
+ force: true
18479
+ });
18480
+ }
18481
+ await this.#failpoint?.("remove-model");
18482
+ if (this.#activeEnablement() !== null) return;
18483
+ if (manifestId !== null) {
18484
+ const paths = layaModelArtifactPaths(this.#root, manifestId);
18485
+ await rm(paths.complete, {
18486
+ recursive: true,
18487
+ force: true
18488
+ });
18489
+ await rm(paths.partial, {
18490
+ recursive: true,
18491
+ force: true
18492
+ });
18493
+ }
18494
+ if (this.#activeEnablement() !== null || manifestId === null) return;
18495
+ this.#store.immediate(() => this.#models.markRemovedWhileDisabled({
18496
+ manifestId,
18497
+ now: this.#now()
18498
+ }));
18499
+ });
18500
+ return this.status();
18501
+ }
18502
+ /**
18503
+ * Immediately before a worker may start: both whole trees are walked and compared with the enabled lock and
18504
+ * manifest. A failing tree is recorded as `integrity`, deleted and reinstalled once from its pinned source; if the
18505
+ * fresh copy fails too, Laya stays `failed` until its person acts. Paths stay inside the daemon.
18506
+ */
18507
+ async verifyForWorkerStart() {
18508
+ const active = this.#activeEnablement();
18509
+ if (active === null) throw layaRefusal("LAYA_NOT_READY", "the Laya judge kind is not enabled");
18510
+ const selection = this.#selectionFor(active);
18511
+ const row = selection === null ? null : this.#models.get(selection.manifest.manifestId);
18512
+ if (selection === null || row?.state !== "ready" || this.#job !== void 0) return {
18513
+ ok: false,
18514
+ failureCode: row?.failureCode ?? null
18515
+ };
18516
+ const walkedEpoch = this.#epoch;
18517
+ const runtimeRoot = this.#runtimeRoot(selection);
18518
+ const modelRoot = layaModelArtifactPaths(this.#root, selection.manifest.manifestId).complete;
18519
+ const runtimeOk = (await verifyRuntimeTree({
18520
+ root: runtimeRoot,
18521
+ files: runtimeFiles(selection)
18522
+ })).ok;
18523
+ const modelOk = (await verifyRuntimeTree({
18524
+ root: modelRoot,
18525
+ files: modelFiles(selection)
18526
+ })).ok;
18527
+ await this.#failpoint?.("worker-start-verify");
18528
+ if (runtimeOk && modelOk) return {
18529
+ ok: true,
18530
+ runtimeRoot,
18531
+ modelRoot
18532
+ };
18533
+ if (walkedEpoch !== this.#epoch || this.#job !== void 0) return {
18534
+ ok: false,
18535
+ failureCode: null
18536
+ };
18537
+ const job = this.#repairJob(active, selection);
18538
+ const settled = this.#repair(job, {
18539
+ runtime: !runtimeOk,
18540
+ model: !modelOk
18541
+ });
18542
+ job.settled = settled.then(() => void 0, () => void 0);
18543
+ this.#job = job;
18544
+ try {
18545
+ return await settled ? {
18546
+ ok: true,
18547
+ runtimeRoot,
18548
+ modelRoot
18549
+ } : {
18550
+ ok: false,
18551
+ failureCode: "integrity"
18552
+ };
18553
+ } finally {
18554
+ if (this.#job === job) this.#job = void 0;
18555
+ }
18556
+ }
18557
+ /** The stable status answer: the DTO `judge model status`, `doctor` and the desktop app read. */
18558
+ status() {
18559
+ const availability = this.availability();
18560
+ const active = this.#activeEnablement();
18561
+ if (active === null) {
18562
+ if (!availability.available) {
18563
+ const blocked = this.#definition.status();
18564
+ return {
18565
+ state: "unavailable",
18566
+ manifestId: blocked.manifestId,
18567
+ runtimeLockDigest: blocked.runtimeLockDigest,
18568
+ bytesVerified: blocked.bytesVerified,
18569
+ totalBytes: blocked.totalBytes,
18570
+ failureCode: null,
18571
+ reason: availability.code,
18572
+ updatedAt: blocked.updatedAt
18573
+ };
18574
+ }
18575
+ const release = this.#releaseSelection();
18576
+ if (release === null) return this.#definition.status();
18577
+ const row = this.#models.get(release.manifest.manifestId);
18578
+ return {
18579
+ state: "idle",
18580
+ manifestId: release.manifest.manifestId,
18581
+ runtimeLockDigest: release.runtimeLock.digest,
18582
+ bytesVerified: row?.state === "ready" ? row.bytesVerified : 0,
18583
+ totalBytes: totalBytes(release),
18584
+ failureCode: null,
18585
+ reason: null,
18586
+ updatedAt: row?.updatedAt ?? null
18587
+ };
18588
+ }
18589
+ const enabled = this.#enabledShape(active);
18590
+ const row = enabled === null ? null : this.#models.get(enabled.manifestId);
18591
+ const answer = (state, bytesVerified, failureCode, reason) => ({
18592
+ state,
18593
+ manifestId: enabled?.manifestId ?? null,
18594
+ runtimeLockDigest: enabled?.runtimeLockDigest ?? null,
18595
+ bytesVerified,
18596
+ totalBytes: enabled?.totalBytes ?? 0,
18597
+ failureCode,
18598
+ reason,
18599
+ updatedAt: row?.updatedAt ?? null
18600
+ });
18601
+ if (!availability.available) return answer("failed", row?.bytesVerified ?? 0, "boundary-failed", availability.code);
18602
+ const job = this.#job;
18603
+ if (job !== void 0 && job.version === active.version && job.epoch === this.#epoch) return answer(job.phase, this.#progress(job), null, null);
18604
+ if (row === null || row.state === "removed") return answer("installing", 0, null, null);
18605
+ return answer(row.state, row.bytesVerified, row.state === "failed" ? row.failureCode : null, null);
18606
+ }
18607
+ /** The doctor's view: the status answer plus the runtime and model states and the boundary reason, never a path. */
18608
+ async doctor() {
18609
+ const status = this.status();
18610
+ const availability = this.availability();
18611
+ const job = this.#job;
18612
+ let runtime = "absent";
18613
+ if (job !== void 0 && job.phase === "installing") runtime = "installing";
18614
+ else if (status.runtimeLockDigest !== null && /^[a-f0-9]{64}$/.test(status.runtimeLockDigest)) runtime = await isDirectory(join(this.#root, "runtime", status.runtimeLockDigest)) ? "installed" : "absent";
18615
+ const row = status.manifestId === null ? null : this.#models.get(status.manifestId);
18616
+ return {
18617
+ state: status.state,
18618
+ manifestId: status.manifestId,
18619
+ runtimeLockDigest: status.runtimeLockDigest,
18620
+ runtime,
18621
+ model: row?.state ?? "absent",
18622
+ boundary: availability.available ? {
18623
+ status: "active",
18624
+ reason: null
18625
+ } : {
18626
+ status: "unavailable",
18627
+ reason: availability.code
18628
+ },
18629
+ failureCode: status.failureCode,
18630
+ bytesVerified: status.bytesVerified,
18631
+ totalBytes: status.totalBytes
18632
+ };
18633
+ }
18634
+ /** Task 8's one worker supervisor; the returned function detaches exactly that supervisor. */
18635
+ attachWorker(worker) {
18636
+ if (this.#worker !== void 0) throw new Error("a Laya worker supervisor is already attached");
18637
+ this.#worker = worker;
18638
+ return () => {
18639
+ if (this.#worker === worker) this.#worker = void 0;
18640
+ };
18641
+ }
18642
+ /**
18643
+ * The selection a worker may start for: the active pointer, only while its document is exactly this release's
18644
+ * reviewed one, with the manager's current epoch. Null before an enablement, after a disable, an update or a stop.
18645
+ */
18646
+ workerSelection() {
18647
+ if (this.#stopped) return null;
18648
+ const active = this.#activeEnablement();
18649
+ if (active === null) return null;
18650
+ const selection = this.#selectionFor(active);
18651
+ if (selection === null) return null;
18652
+ return {
18653
+ epoch: this.#epoch,
18654
+ version: active.version,
18655
+ digest: active.digest,
18656
+ manifestId: selection.manifest.manifestId,
18657
+ promptTemplates: selection.manifest.promptTemplates
18658
+ };
18659
+ }
18660
+ /**
18661
+ * D11: a worker whose per-start boundary proof failed, or whose verified runtime would not load, leaves Laya
18662
+ * `failed` with that closed code until its person acts — no later request starts another child for it. The write is
18663
+ * made only while the selection that worker verified is still the current one, in the same transaction.
18664
+ */
18665
+ recordWorkerFailure(started, failureCode) {
18666
+ try {
18667
+ this.#store.immediate(() => {
18668
+ const current = this.workerSelection();
18669
+ if (current === null || current.epoch !== started.epoch || current.version !== started.version || current.digest !== started.digest) return;
18670
+ const row = this.#models.get(started.manifestId);
18671
+ if (row?.state !== "ready") return;
18672
+ this.#models.write({
18673
+ manifestId: started.manifestId,
18674
+ state: "failed",
18675
+ bytesVerified: row.bytesVerified,
18676
+ failureCode,
18677
+ now: this.#now()
18678
+ });
18679
+ });
18680
+ } catch {}
18681
+ }
18682
+ /**
18683
+ * Task 8's supervisor registers each in-flight worker request by its E reservation. The returned signal fires when
18684
+ * an E purge terminalises exactly that reservation; nothing else is touched.
18685
+ */
18686
+ trackWork(reservationId) {
18687
+ const controller = new AbortController();
18688
+ const work = {
18689
+ reservationId,
18690
+ controller
18691
+ };
18692
+ this.#tracked.set(reservationId, work);
18693
+ return {
18694
+ signal: controller.signal,
18695
+ release: () => {
18696
+ if (this.#tracked.get(reservationId) === work) this.#tracked.delete(reservationId);
18697
+ }
18698
+ };
18699
+ }
18700
+ /**
18701
+ * E's purge participant calls this inside the purge transaction, after its recovery transition. Only tracked work
18702
+ * whose own reservation that transition settled or released is stopped: an account, rule, hold, visibility,
18703
+ * expiry or disable-all purge never touches an installation-wide verified tree or another scope's work.
18704
+ */
18705
+ onWorkPurged() {
18706
+ for (const work of [...this.#tracked.values()]) {
18707
+ const row = this.#store.database.prepare("SELECT state FROM judge_budget_reservations WHERE id = ?").get(work.reservationId);
18708
+ if (row !== void 0 && (row.state === "reserved" || row.state === "in-flight")) continue;
18709
+ this.#tracked.delete(work.reservationId);
18710
+ work.controller.abort(new ArtifactWorkStale());
18711
+ }
18712
+ }
18713
+ /** Owner stop: no job outlives the database it writes to. */
18714
+ async stop() {
18715
+ this.#stopped = true;
18716
+ const job = this.#fence();
18717
+ for (const work of this.#tracked.values()) work.controller.abort(new ArtifactWorkStale());
18718
+ this.#tracked.clear();
18719
+ await job?.settled.catch(() => void 0);
18720
+ await this.#worker?.stop();
18721
+ }
18722
+ #fence() {
18723
+ this.#epoch += 1;
18724
+ const job = this.#job;
18725
+ job?.controller.abort(new ArtifactWorkStale());
18726
+ return job;
18727
+ }
18728
+ #locked(edge, work) {
18729
+ return this.#mutex.run(async () => {
18730
+ await this.#failpoint?.(edge);
18731
+ return work();
18732
+ });
18733
+ }
18734
+ #activeEnablement() {
18735
+ const row = this.#store.database.prepare(`SELECT active.version AS version, versions.document AS document, versions.digest AS digest
18736
+ FROM active_versions AS active
18737
+ JOIN judge_kind_versions AS versions
18738
+ ON versions.kind = active.object_id AND versions.version = active.version
18739
+ WHERE active.kind = 'judge-kind' AND active.object_id = ?`).get(JUDGE_KIND_LAYA);
18740
+ if (row === void 0) return null;
18741
+ let document;
18742
+ try {
18743
+ document = JSON.parse(row.document);
18744
+ } catch {
18745
+ document = null;
18746
+ }
18747
+ return {
18748
+ version: row.version,
18749
+ digest: row.digest,
18750
+ document
18751
+ };
18752
+ }
18753
+ /** The active document, only when it is exactly this release's reviewed document; an update leaves it null. */
18754
+ #selectionFor(active) {
18755
+ try {
18756
+ const release = this.#definition.preview({ version: active.version });
18757
+ return layaUpdateReason(active.document, release) === null ? release : null;
18758
+ } catch {
18759
+ return null;
18760
+ }
18761
+ }
18762
+ #releaseSelection() {
18763
+ try {
18764
+ return this.#definition.preview({ version: 1 });
18765
+ } catch {
18766
+ return null;
18767
+ }
18768
+ }
18769
+ #manifestId() {
18770
+ return this.#releaseSelection()?.manifest.manifestId ?? this.#definition.status().manifestId;
18771
+ }
18772
+ /** The content-free identity of an enabled document, read even when this release no longer matches it. */
18773
+ #enabledShape(active) {
18774
+ const selection = this.#selectionFor(active);
18775
+ if (selection !== null) return {
18776
+ manifestId: selection.manifest.manifestId,
18777
+ runtimeLockDigest: selection.runtimeLock.digest,
18778
+ totalBytes: totalBytes(selection)
18779
+ };
18780
+ const document = active.document;
18781
+ const manifestId = document?.manifest?.manifestId;
18782
+ const digest = document?.runtimeLock?.digest;
18783
+ if (typeof manifestId !== "string" || typeof digest !== "string") return null;
18784
+ return {
18785
+ manifestId,
18786
+ runtimeLockDigest: digest,
18787
+ totalBytes: bytesOf([...document?.manifest?.files ?? [], ...document?.runtimeLock?.platform?.files ?? []])
18788
+ };
18789
+ }
18790
+ #runtimeRoot(selection) {
18791
+ const digest = selection.runtimeLock.digest;
18792
+ if (!/^[a-f0-9]{64}$/.test(digest)) throw new ArtifactFailure("runtime-failed");
18793
+ return join(this.#root, "runtime", digest);
18794
+ }
18795
+ #progress(job) {
18796
+ let model = 0;
18797
+ for (const bytes of job.modelProgress.values()) model += bytes;
18798
+ return Math.min(job.runtimeBytes + model, totalBytes(job.selection));
18799
+ }
18800
+ /** The synchronous recheck every post-await step makes: same pointer version, same document, same epoch. */
18801
+ #assertCurrent(job) {
18802
+ if (this.#stopped || job.epoch !== this.#epoch || job.controller.signal.aborted) throw new ArtifactWorkStale();
18803
+ const active = this.#activeEnablement();
18804
+ if (active === null || active.version !== job.version || active.digest !== job.digest) throw new ArtifactWorkStale();
18805
+ }
18806
+ #isCurrent(job) {
18807
+ try {
18808
+ this.#assertCurrent(job);
18809
+ return true;
18810
+ } catch {
18811
+ return false;
18812
+ }
18813
+ }
18814
+ #write(job, state, bytesVerified, failureCode) {
18815
+ this.#store.immediate(() => {
18816
+ this.#assertCurrent(job);
18817
+ this.#models.write({
18818
+ manifestId: job.selection.manifest.manifestId,
18819
+ state,
18820
+ bytesVerified,
18821
+ ...state === "ready" ? { verifiedAt: this.#now() } : {},
18822
+ ...failureCode === void 0 ? {} : { failureCode },
18823
+ now: this.#now()
18824
+ });
18825
+ });
18826
+ }
18827
+ #fenceFor(job) {
18828
+ return {
18829
+ assertCurrent: async (stage) => {
18830
+ if (stage === "promotion") await this.#failpoint?.("model-promotion");
18831
+ this.#assertCurrent(job);
18832
+ },
18833
+ withArtifactLock: (work) => this.#mutex.run(work)
18834
+ };
18835
+ }
18836
+ async #install(job) {
18837
+ try {
18838
+ await this.#locked("install-start", async () => {
18839
+ this.#write(job, "installing", 0);
18840
+ });
18841
+ const selection = job.selection;
18842
+ const runtimeRoot = this.#runtimeRoot(selection);
18843
+ if (!(await verifyRuntimeTree({
18844
+ root: runtimeRoot,
18845
+ files: runtimeFiles(selection)
18846
+ })).ok) {
18847
+ this.#assertCurrent(job);
18848
+ await this.#installRuntime(job, { replace: await isDirectory(runtimeRoot) });
18849
+ }
18850
+ this.#assertCurrent(job);
18851
+ job.runtimeBytes = bytesOf(selection.runtimeLock.platform.files);
18852
+ this.#write(job, "downloading", job.runtimeBytes);
18853
+ job.phase = "downloading";
18854
+ const modelRoot = layaModelArtifactPaths(this.#root, selection.manifest.manifestId).complete;
18855
+ if (!(await verifyRuntimeTree({
18856
+ root: modelRoot,
18857
+ files: modelFiles(selection)
18858
+ })).ok) {
18859
+ this.#assertCurrent(job);
18860
+ await this.#downloadModel(job, { replace: await isDirectory(modelRoot) });
18861
+ }
18862
+ job.phase = "verifying";
18863
+ const runtime = await verifyRuntimeTree({
18864
+ root: runtimeRoot,
18865
+ files: runtimeFiles(selection)
18866
+ });
18867
+ const model = await verifyRuntimeTree({
18868
+ root: modelRoot,
18869
+ files: modelFiles(selection)
18870
+ });
18871
+ if (!runtime.ok || !model.ok) throw new ArtifactFailure("integrity");
18872
+ await this.#locked("model-ready", async () => {
18873
+ this.#write(job, "ready", totalBytes(selection));
18874
+ await this.#removeObsoleteTrees(job);
18875
+ });
18876
+ } catch (error) {
18877
+ this.#recordFailure(job, error);
18878
+ }
18879
+ }
18880
+ #recordFailure(job, error) {
18881
+ if (error instanceof ArtifactWorkStale || !this.#isCurrent(job)) return;
18882
+ const failureCode = error instanceof ArtifactFailure ? error.failureCode : job.phase === "installing" ? "runtime-failed" : job.phase === "downloading" ? "download-failed" : "integrity";
18883
+ try {
18884
+ this.#write(job, "failed", this.#progress(job), failureCode);
18885
+ } catch {}
18886
+ }
18887
+ /**
18888
+ * Fetches every SRI-pinned archive, extracts only the selected platform's listed files into a staging tree that
18889
+ * the strict installer verifies, then promotes that tree inside the artifact lock after the exact recheck.
18890
+ */
18891
+ async #installRuntime(job, options) {
18892
+ const selection = job.selection;
18893
+ const runtimeRoot = this.#runtimeRoot(selection);
18894
+ const staging = `${runtimeRoot}.staging`;
18895
+ const files = runtimeFiles(selection);
18896
+ await ownerOnlyDirectory(join(this.#root, "runtime"));
18897
+ const free = await (this.#freeBytes ?? (() => availableBytes(join(this.#root, "runtime"))))();
18898
+ if (!Number.isSafeInteger(free) || free < Math.ceil(bytesOf(files) * 1.1)) throw new ArtifactFailure("runtime-failed");
18899
+ const fetcher = this.#fetcher(job);
18900
+ const packages = [];
18901
+ for (const pkg of selection.runtimeLock.packages) {
18902
+ if (!files.some((file) => file.packageName === pkg.name)) continue;
18903
+ this.#assertCurrent(job);
18904
+ const bytes = await fetcher.fetchRuntimeArchive(pkg, { signal: job.controller.signal });
18905
+ packages.push({
18906
+ packageName: pkg.name,
18907
+ source: (async function* () {
18908
+ yield bytes;
18909
+ })()
18910
+ });
18911
+ }
18912
+ this.#assertCurrent(job);
18913
+ await rm(staging, {
18914
+ recursive: true,
18915
+ force: true
18916
+ });
18917
+ try {
18918
+ const installed = await installRuntime({
18919
+ runtimeDirectory: staging,
18920
+ lockDigest: selection.runtimeLock.digest,
18921
+ packages,
18922
+ files
18923
+ });
18924
+ await this.#locked("runtime-promotion", async () => {
18925
+ this.#assertCurrent(job);
18926
+ if (options.replace) await rm(runtimeRoot, {
18927
+ recursive: true,
18928
+ force: true
18929
+ });
18930
+ await rename(installed.root, runtimeRoot);
18931
+ });
18932
+ } finally {
18933
+ await rm(staging, {
18934
+ recursive: true,
18935
+ force: true
18936
+ });
18937
+ }
18938
+ }
18939
+ async #downloadModel(job, options) {
18940
+ const selection = job.selection;
18941
+ const paths = layaModelArtifactPaths(this.#root, selection.manifest.manifestId);
18942
+ if (options.replace) await this.#mutex.run(async () => {
18943
+ this.#assertCurrent(job);
18944
+ await rm(paths.complete, {
18945
+ recursive: true,
18946
+ force: true
18947
+ });
18948
+ });
18949
+ await new LayaModelDownloader({
18950
+ dataDirectory: this.#root,
18951
+ manifest: selection.manifest,
18952
+ fetcher: this.#fetcher(job),
18953
+ fence: this.#fenceFor(job)
18954
+ }).download({
18955
+ signal: job.controller.signal,
18956
+ ...this.#freeBytes === void 0 ? {} : { freeBytes: this.#freeBytes }
18957
+ });
18958
+ }
18959
+ #fetcher(job) {
18960
+ return new ProgressReportingArtifactFetcher({
18961
+ manifest: job.selection.manifest,
18962
+ runtimePackages: job.selection.runtimeLock.packages,
18963
+ resolver: this.#network.resolver,
18964
+ tcpConnect: this.#network.tcpConnect,
18965
+ tlsConnect: this.#network.tlsConnect
18966
+ }, job.modelProgress);
18967
+ }
18968
+ #repairJob(active, selection) {
18969
+ return {
18970
+ epoch: this.#epoch,
18971
+ version: active.version,
18972
+ digest: active.digest,
18973
+ selection,
18974
+ controller: new AbortController(),
18975
+ phase: "installing",
18976
+ runtimeBytes: 0,
18977
+ modelProgress: /* @__PURE__ */ new Map(),
18978
+ settled: Promise.resolve()
18979
+ };
18980
+ }
18981
+ /** One repair per failing tree; the fresh copy must verify or Laya records `integrity` and stays failed. */
18982
+ async #repair(job, failing) {
18983
+ const selection = job.selection;
18984
+ const runtimeRoot = this.#runtimeRoot(selection);
18985
+ const modelRoot = layaModelArtifactPaths(this.#root, selection.manifest.manifestId).complete;
18986
+ try {
18987
+ await this.#locked("repair-delete", async () => {
18988
+ this.#assertCurrent(job);
18989
+ if (failing.runtime) await rm(runtimeRoot, {
18990
+ recursive: true,
18991
+ force: true
18992
+ });
18993
+ if (failing.model) await rm(modelRoot, {
18994
+ recursive: true,
18995
+ force: true
18996
+ });
18997
+ this.#write(job, "failed", 0, "integrity");
18998
+ });
18999
+ if (failing.runtime) await this.#installRuntime(job, { replace: false });
19000
+ job.runtimeBytes = bytesOf(selection.runtimeLock.platform.files);
19001
+ job.phase = "downloading";
19002
+ if (failing.model) await this.#downloadModel(job, { replace: false });
19003
+ job.phase = "verifying";
19004
+ const runtime = await verifyRuntimeTree({
19005
+ root: runtimeRoot,
19006
+ files: runtimeFiles(selection)
19007
+ });
19008
+ const model = await verifyRuntimeTree({
19009
+ root: modelRoot,
19010
+ files: modelFiles(selection)
19011
+ });
19012
+ if (!runtime.ok || !model.ok) throw new ArtifactFailure("integrity");
19013
+ await this.#locked("model-ready", async () => {
19014
+ this.#write(job, "ready", totalBytes(selection));
19015
+ await this.#removeObsoleteTrees(job);
19016
+ });
19017
+ return true;
19018
+ } catch (error) {
19019
+ if (error instanceof ArtifactWorkStale || !this.#isCurrent(job)) return false;
19020
+ try {
19021
+ this.#write(job, "failed", 0, "integrity");
19022
+ } catch {}
19023
+ return false;
19024
+ }
19025
+ }
19026
+ /**
19027
+ * D11 and E2-5: once a selection's model is ready, a tree no enabled entry names is obsolete. Inside the
19028
+ * `model-ready` lock, every `runtime/` and `models/` entry but the selection's own two is deleted, partial and staging
19029
+ * trees included, and the sweep stops at its first recheck that finds the job fenced by a disable, update or stop.
19030
+ */
19031
+ #removeObsoleteTrees(job) {
19032
+ const named = {
19033
+ runtime: job.selection.runtimeLock.digest,
19034
+ models: job.selection.manifest.manifestId
19035
+ };
19036
+ return this.#sweepTrees((directory, name) => name === named[directory], () => this.#isCurrent(job));
19037
+ }
19038
+ /**
19039
+ * `judge model remove`'s sweep of every verified tree but this release's own, which the removal then deletes with
19040
+ * its unverified copies. It stops at its first recheck that finds an enablement claimed meanwhile.
19041
+ */
19042
+ #removeEarlierVerifiedTrees() {
19043
+ const own = {
19044
+ runtime: this.#releaseSelection()?.runtimeLock.digest,
19045
+ models: this.#manifestId()
19046
+ };
19047
+ return this.#sweepTrees((directory, name) => name.endsWith(".partial") || name.endsWith(".staging") || name === own[directory], () => this.#activeEnablement() === null);
19048
+ }
19049
+ /**
19050
+ * Deletes, one at a time in raw-byte name order, every entry under `runtime/` and `models/` that `keep` does not
19051
+ * name, rechecking `current` after every await: a tightening or a claim that lands meanwhile stops the sweep before
19052
+ * its next deletion. A deletion that fails is left for the next sweep. Resolves whether the sweep finished. Its
19053
+ * callers hold the artifact lock.
19054
+ */
19055
+ async #sweepTrees(keep, current) {
19056
+ for (const directory of ["runtime", "models"]) {
19057
+ let names;
19058
+ try {
19059
+ names = await readdir(join(this.#root, directory));
19060
+ } catch {
19061
+ names = [];
19062
+ }
19063
+ if (!current()) return false;
19064
+ names.sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
19065
+ for (const name of names) {
19066
+ if (keep(directory, name)) continue;
19067
+ await rm(join(this.#root, directory, name), {
19068
+ recursive: true,
19069
+ force: true
19070
+ }).catch(() => void 0);
19071
+ await this.#failpoint?.("tree-sweep");
19072
+ if (!current()) return false;
19073
+ }
19074
+ }
19075
+ return true;
19076
+ }
19077
+ /** Deletes every partial and staging tree beneath the artifact root, and never a verified one. */
19078
+ async #removeUnverified() {
19079
+ for (const directory of ["runtime", "models"]) {
19080
+ const parent = join(this.#root, directory);
19081
+ let names;
19082
+ try {
19083
+ names = await readdir(parent);
19084
+ } catch {
19085
+ continue;
19086
+ }
19087
+ for (const name of names) if (name.endsWith(".partial") || name.endsWith(".staging")) await rm(join(parent, name), {
19088
+ recursive: true,
19089
+ force: true
19090
+ });
19091
+ }
19092
+ }
19093
+ };
19094
+ function unavailableResult(code) {
19095
+ return {
19096
+ kind: "unavailable",
19097
+ code,
19098
+ phase: "E2"
19099
+ };
19100
+ }
19101
+ //#endregion
19102
+ //#region src/runtime/lifecycle.ts
19103
+ var EventLifecycle = class {
19104
+ store;
19105
+ now;
19106
+ constructor(store, now = Date.now) {
16208
19107
  this.store = store;
16209
19108
  this.now = now;
16210
19109
  }
@@ -16799,10 +19698,6 @@ function closedReason(result) {
16799
19698
  if (result.kind === "answer") return "completed";
16800
19699
  return result.code.toLowerCase().replaceAll("_", "-");
16801
19700
  }
16802
- /**
16803
- * The sole agentic execution coordinator. It owns no ledger transition and no socket path: Task 5's budget
16804
- * functions and Task 6's sealed transport remain the only owners of those effects.
16805
- */
16806
19701
  var JudgeCoordinator = class {
16807
19702
  #store;
16808
19703
  #taint;
@@ -16838,6 +19733,7 @@ var JudgeCoordinator = class {
16838
19733
  channel: input.rule.source.channel,
16839
19734
  classification: envelope.classification
16840
19735
  });
19736
+ if (agentic.judge.kind === "laya") return this.#evaluateLaya(input, agentic.judge, envelope);
16841
19737
  let hostedApiKey;
16842
19738
  try {
16843
19739
  hostedApiKey = agentic.judge.kind === "typesafe" ? await this.#hostedApiKeyFor({
@@ -16974,6 +19870,127 @@ var JudgeCoordinator = class {
16974
19870
  }
16975
19871
  };
16976
19872
  }
19873
+ /**
19874
+ * E2's Laya adapter, in E's order: the deterministic prefilter, the minimised envelope and the taint flush have run;
19875
+ * here the database owner's Laya executor checks the enabled entry and template and bounds the request before any
19876
+ * reservation, E reserves the exact request payload, E's gate moves the reservation in flight immediately before the
19877
+ * one frame is written, and only the worker's bounded value and token count come back for E's final settlement.
19878
+ * Every failure is a closed reason and no-match: pre-I/O E releases the reservation, post-I/O E settles it.
19879
+ */
19880
+ async #evaluateLaya(input, judge, envelope) {
19881
+ const agentic = input.rule.agentic;
19882
+ if (agentic === void 0) return { kind: "not-applicable" };
19883
+ const executor = layaJudgeExecutorFor(this.#store.database);
19884
+ if (executor === void 0) return {
19885
+ kind: "closed",
19886
+ reason: "adapter-reference-blocked",
19887
+ reservationId: null
19888
+ };
19889
+ const laya = executor.prepare({
19890
+ judge,
19891
+ state: envelope.state,
19892
+ instructions: agentic.question
19893
+ });
19894
+ if (!laya.ok) return {
19895
+ kind: "closed",
19896
+ reason: laya.code.toLowerCase(),
19897
+ reservationId: null
19898
+ };
19899
+ let reservation;
19900
+ try {
19901
+ reservation = reserveJudgeWork({
19902
+ store: this.#store,
19903
+ id: this.#newId(),
19904
+ workId: `judge:${input.eventId}:${input.ruleId}:${input.ruleVersion}`,
19905
+ scope: {
19906
+ scope: "source-owned",
19907
+ source: input.rule.source.channel,
19908
+ accountId: input.accountId,
19909
+ eventId: input.eventId,
19910
+ ruleId: input.ruleId,
19911
+ ruleVersion: input.ruleVersion,
19912
+ judgeId: judge.judgeId,
19913
+ judgeVersion: judge.version
19914
+ },
19915
+ judge,
19916
+ limits: {
19917
+ callsPerRollingHour: agentic.callsPerRollingHour,
19918
+ inputTokensPerRolling30Days: agentic.inputTokensPerRolling30Days,
19919
+ concurrency: agentic.concurrency,
19920
+ maxInputTokens: agentic.maxInputTokens
19921
+ },
19922
+ finalEnvelope: laya.request.body,
19923
+ switchGeneration: input.switchGeneration,
19924
+ now: this.#now()
19925
+ });
19926
+ } catch (error) {
19927
+ const code = error instanceof Error && "code" in error ? String(error.code) : "budget-refused";
19928
+ if (code === "JUDGE_PAUSED") return {
19929
+ kind: "deferred",
19930
+ reservationId: null
19931
+ };
19932
+ if (code === "JUDGE_WORK_OWNER_HELD") return { kind: "owner-held" };
19933
+ return {
19934
+ kind: "closed",
19935
+ reason: code,
19936
+ reservationId: null
19937
+ };
19938
+ }
19939
+ const ioGate = new JudgeReservationIoGate({
19940
+ store: this.#store,
19941
+ reservationId: reservation.id,
19942
+ judgeKind: judge.kind,
19943
+ now: this.#now
19944
+ });
19945
+ const result = await executor.execute(laya.request, {
19946
+ reservationId: reservation.id,
19947
+ finalGate: () => ioGate.finalGate("write")
19948
+ });
19949
+ if (result.kind === "answer") return {
19950
+ kind: "completed",
19951
+ outcome: uncertainOutcome(input.rule, result.value),
19952
+ value: result.value,
19953
+ valueLabel: "probability",
19954
+ actualInputTokens: result.inputTokens,
19955
+ reservationId: reservation.id,
19956
+ metadata: {
19957
+ judgeId: judge.judgeId,
19958
+ judgeVersion: judge.version,
19959
+ provider: judge.provider,
19960
+ model: judge.model,
19961
+ promptTemplate: judge.promptTemplate,
19962
+ threshold: agentic.threshold,
19963
+ value: result.value,
19964
+ valueLabel: "probability"
19965
+ }
19966
+ };
19967
+ const disposition = finalizeJudgeTransportRefusal({
19968
+ store: this.#store,
19969
+ reservationId: reservation.id,
19970
+ result: result.code === "laya-timeout" ? "timeout" : "unknown",
19971
+ now: this.#now()
19972
+ });
19973
+ if (ioGate.firstGateError !== void 0) {
19974
+ if (ioGate.firstGateError instanceof JudgeBudgetError && ioGate.firstGateError.code === "JUDGE_PAUSED") return {
19975
+ kind: "deferred",
19976
+ reservationId: reservation.id
19977
+ };
19978
+ return {
19979
+ kind: "closed",
19980
+ reason: ioGate.firstGateError instanceof Error && "code" in ioGate.firstGateError ? String(ioGate.firstGateError.code) : "authority-changed",
19981
+ reservationId: reservation.id
19982
+ };
19983
+ }
19984
+ if (disposition === "released" && result.code === "laya-stopped") return {
19985
+ kind: "deferred",
19986
+ reservationId: reservation.id
19987
+ };
19988
+ return {
19989
+ kind: "closed",
19990
+ reason: result.code.toLowerCase(),
19991
+ reservationId: reservation.id
19992
+ };
19993
+ }
16977
19994
  };
16978
19995
  //#endregion
16979
19996
  //#region src/runtime/evaluate.ts
@@ -20167,7 +23184,8 @@ function createOwnerB3ControlService(options) {
20167
23184
  now,
20168
23185
  ...judgeActivations === void 0 ? {} : { activations: judgeActivations },
20169
23186
  ...options.judgeTest === void 0 ? {} : { judgeTest: { test: options.judgeTest.test.bind(options.judgeTest) } },
20170
- ...options.holdResolver === void 0 ? {} : { holdResolver: options.holdResolver }
23187
+ ...options.holdResolver === void 0 ? {} : { holdResolver: options.holdResolver },
23188
+ ...options.laya === void 0 ? {} : { laya: options.laya }
20171
23189
  });
20172
23190
  const target = (pendingOrigin) => new B3TargetTestControl({
20173
23191
  store: options.store,
@@ -20228,7 +23246,7 @@ function createOwnerB3ControlService(options) {
20228
23246
  }
20229
23247
  }
20230
23248
  switch (request.operation) {
20231
- case "approvalView": return approvalViewFromOwner(options.store, options.activations, now, request.args);
23249
+ case "approvalView": return approvalViewFromOwner(options.store, options.activations, now, request.args, options.laya !== void 0);
20232
23250
  case "ruleTest": return runCatalogueRuleTest({
20233
23251
  document: request.args.document,
20234
23252
  catalogue: request.args.catalogue
@@ -20396,16 +23414,21 @@ function terminalSecretResult(receipt) {
20396
23414
  terminalReveal: reveal
20397
23415
  };
20398
23416
  }
20399
- async function approvalViewFromOwner(store, activations, now, args) {
23417
+ /**
23418
+ * The authoritative approval view. With E2's artifact manager composed the owner answers B3 control v3, whose view of
23419
+ * a rule naming a Laya judge also carries D11's Laya limits, from the same reviewed assets as the enablement.
23420
+ */
23421
+ async function approvalViewFromOwner(store, activations, now, args, layaJudges = false) {
20400
23422
  const approvalId = requiredText(args.approvalId, "approval id");
20401
23423
  const digest = requiredText(args.digest, "approval digest");
20402
23424
  const row = store.database.prepare("SELECT id, document, digest, status FROM activation_intents WHERE approval_id = ?").get(approvalId);
20403
23425
  if (row === void 0 || row.status !== "pending") return { kind: "STALE_APPROVAL" };
20404
23426
  if (row.digest !== digest) return { kind: "DIGEST_MISMATCH" };
23427
+ const preview = normaliseActivationDocument(JSON.parse(row.document));
23428
+ const layaJudge = layaJudges ? layaJudgePreviewFor(preview, layaJudgeTermsFor(layaEnablementInputsFor(store.database))) : void 0;
20405
23429
  const nextExpiry = now() + 3e5;
20406
23430
  const challenge = await activations.issueApprovalViewChallenge(approvalId, nextExpiry);
20407
23431
  if (challenge === null) return { kind: "CHALLENGE_EXPIRED" };
20408
- const preview = normaliseActivationDocument(JSON.parse(row.document));
20409
23432
  return {
20410
23433
  kind: "READY",
20411
23434
  approvalId,
@@ -20423,7 +23446,8 @@ async function approvalViewFromOwner(store, activations, now, args) {
20423
23446
  digest: row.digest,
20424
23447
  challenge,
20425
23448
  expiresAt: nextExpiry
20426
- }
23449
+ },
23450
+ ...layaJudge === void 0 ? {} : { layaJudge }
20427
23451
  };
20428
23452
  }
20429
23453
  /**
@@ -20771,6 +23795,34 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
20771
23795
  }
20772
23796
  });
20773
23797
  };
23798
+ const layaInputs = {
23799
+ ...options.laya?.assets === void 0 ? {} : { layaAssetSource: options.laya.assets },
23800
+ ...options.laya?.platform === void 0 ? {} : { layaPlatformProbe: options.laya.platform }
23801
+ };
23802
+ const layaArtifacts = new LayaArtifactManager({
23803
+ store: database,
23804
+ artifactRoot: join(options.dataDir ?? core.paths.dataDir, "events", JUDGE_KIND_LAYA),
23805
+ inputs: layaInputs,
23806
+ network: options.laya?.network,
23807
+ freeBytes: options.laya?.freeBytes,
23808
+ failpoint: options.laya?.failpoint,
23809
+ audit: core.audit,
23810
+ now
23811
+ });
23812
+ const unregisterLayaPurge = registerJudgeWorkPurgeListener(database.database, layaArtifacts);
23813
+ const unregisterLayaInputs = registerLayaEnablementInputs(database.database, layaInputs);
23814
+ const layaWorker = new LayaWorkerSupervisor({
23815
+ artifacts: layaArtifacts,
23816
+ startWorker: options.laya?.startWorker
23817
+ });
23818
+ const unregisterLayaWorker = registerLayaJudgeExecutor(database.database, layaWorker);
23819
+ onDatabase({ close: () => {
23820
+ unregisterLayaWorker();
23821
+ unregisterLayaInputs();
23822
+ unregisterLayaPurge();
23823
+ layaWorker.close();
23824
+ database.close();
23825
+ } });
20774
23826
  const activations = new ActivationRuntime({
20775
23827
  store: database,
20776
23828
  approvals: core.approvals,
@@ -20909,7 +23961,11 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
20909
23961
  retainedContentHooks: whatsappComposition.retainedContentHooks,
20910
23962
  now,
20911
23963
  ...options.newIntentId === void 0 ? {} : { newIntentId: options.newIntentId },
20912
- assertObjectSecretReady: b3Ports.assertObjectSecretReady
23964
+ assertObjectSecretReady: b3Ports.assertObjectSecretReady,
23965
+ ...layaInputs,
23966
+ onJudgeKindEnabled: ({ kind }) => {
23967
+ if (kind === "laya") layaArtifacts.ensureInstalled();
23968
+ }
20913
23969
  });
20914
23970
  const scheduler = new EventScheduler({
20915
23971
  store: database,
@@ -21063,10 +24119,11 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
21063
24119
  }),
21064
24120
  whatsappVisibilityFence: whatsappComposition.visibilityFence
21065
24121
  }),
24122
+ laya: layaArtifacts,
21066
24123
  now
21067
24124
  });
21068
24125
  const doctorReport = async () => {
21069
- return projectE9Doctor({
24126
+ return projectE2Doctor({
21070
24127
  database: database.database,
21071
24128
  installationId: database.installationId,
21072
24129
  lifecycle: lifecycle.status(),
@@ -21075,6 +24132,7 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
21075
24132
  config: core.config,
21076
24133
  approvals: core.approvals,
21077
24134
  decryptSourceState: decryptSourceStage,
24135
+ laya: layaArtifacts,
21078
24136
  now
21079
24137
  });
21080
24138
  };
@@ -21102,7 +24160,12 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
21102
24160
  try {
21103
24161
  await scheduler.stop();
21104
24162
  await control?.close();
24163
+ await layaWorker.close();
24164
+ await layaArtifacts.stop();
21105
24165
  } finally {
24166
+ unregisterLayaWorker();
24167
+ unregisterLayaInputs();
24168
+ unregisterLayaPurge();
21106
24169
  database.close();
21107
24170
  await removeInstance(paths, instance.instanceId);
21108
24171
  await rm(paths.controlToken, { force: true });
@@ -21119,7 +24182,13 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
21119
24182
  database: database.database,
21120
24183
  now: now()
21121
24184
  }));
24185
+ await withEventWriterLease(writerQuiescer, () => layaArtifacts.reconcileRelease(() => new JudgeKinds({
24186
+ store: database,
24187
+ now,
24188
+ activations
24189
+ }).disable(JUDGE_KIND_LAYA)));
21122
24190
  await withEventWriterLease(writerQuiescer, () => recoverActivations(activations));
24191
+ layaArtifacts.resume().catch(() => void 0);
21123
24192
  await writeToken(paths, token);
21124
24193
  control = await (options.startControlServer ?? startControlServer)({
21125
24194
  endpoint: instance.endpoint,
@@ -21204,10 +24273,10 @@ async function handleControl(owner, lifecycle, database, activations, dispatcher
21204
24273
  case OWNER_CONTROL_WIRES.ruleShow: return ruleShow(database, requiredText(request.args.ruleId, "rule id"));
21205
24274
  case OWNER_CONTROL_WIRES.createRule:
21206
24275
  case OWNER_CONTROL_WIRES.updateRule: return createRule(database, request.args.document);
21207
- case OWNER_CONTROL_WIRES.enableRule: return activations.prepareRule({
24276
+ case OWNER_CONTROL_WIRES.enableRule: return domainAwaited(() => activations.prepareRule({
21208
24277
  ruleId: requiredText(request.args.ruleId, "rule id"),
21209
24278
  version: requiredVersion(request.args.version, "rule version")
21210
- });
24279
+ }));
21211
24280
  case OWNER_CONTROL_WIRES.disableRule:
21212
24281
  case OWNER_CONTROL_WIRES.removeRule: return disableRule(database, activations, requiredText(request.args.ruleId, "rule id"));
21213
24282
  case OWNER_CONTROL_WIRES.targetsList: return targetRows(database);
@@ -21252,6 +24321,15 @@ function domain(work) {
21252
24321
  throw error;
21253
24322
  }
21254
24323
  }
24324
+ /** `domain` for an operation that awaits: a refusal is the caller's input, with the same fixed reason. */
24325
+ async function domainAwaited(work) {
24326
+ try {
24327
+ return await work();
24328
+ } catch (error) {
24329
+ if (error instanceof EventDomainError) throw new CommsError("BAD_DATA", error.message, { details: { reason: error.code } });
24330
+ throw error;
24331
+ }
24332
+ }
21255
24333
  function ruleRows(database) {
21256
24334
  return database.database.prepare("SELECT rule_id, version, digest, state, approval_id, authorization_activation_id FROM rule_versions ORDER BY rule_id, version").all();
21257
24335
  }
@@ -21263,8 +24341,9 @@ function ruleShow(database, ruleId) {
21263
24341
  document: JSON.parse(row.document)
21264
24342
  }));
21265
24343
  }
24344
+ /** The owner composes E2's artifact manager, so the rules it keeps carry B3 control v3's judge documents. */
21266
24345
  function createRule(database, document) {
21267
- return domain(() => new ImmutableVersions(database.database).createRule(document));
24346
+ return domain(() => ImmutableVersions.forJudgeContract(database.database, "b3-control-v3").createRule(document));
21268
24347
  }
21269
24348
  function targetRows(database) {
21270
24349
  return database.database.prepare("SELECT target_id, version, digest, revoked_at FROM target_versions ORDER BY target_id, version").all();
@@ -21823,7 +24902,7 @@ async function run(argv, deps = {}) {
21823
24902
  const parsed = jsonArgument(document, "budget document");
21824
24903
  print(await humanB3("updateBudget", { document: parsed }, () => updateBudget(parsed, controlOptions(program))));
21825
24904
  });
21826
- judge.command("kinds").description("list disabled/enabled kinds and the typed Laya E2 seam").command("list").action(async () => {
24905
+ judge.command("kinds").description("list each judge kind as disabled or enabled, or Laya as unavailable here with its reason").command("list").action(async () => {
21827
24906
  ran = true;
21828
24907
  print(await judgeKindsList(controlOptions(program)));
21829
24908
  });
@@ -21836,7 +24915,7 @@ async function run(argv, deps = {}) {
21836
24915
  ran = true;
21837
24916
  print(await humanB3("disableJudgeKind", { kind }, () => disableJudgeKind(kind, controlOptions(program))));
21838
24917
  });
21839
- const judgeModel = judge.command("model").description("local Laya model controls, which answer that Laya is not available yet");
24918
+ const judgeModel = judge.command("model").description("report the local Laya runtime and model state, or remove its files while Laya is disabled");
21840
24919
  judgeModel.command("status").action(async () => {
21841
24920
  ran = true;
21842
24921
  print(await judgeModelStatus(controlOptions(program)));