@opendatalabs/personal-server-ts-server 1.8.0 → 1.9.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.
@@ -105697,6 +105697,11 @@ var ServerNotConfiguredError = class extends ProtocolError {
105697
105697
  super(500, "SERVER_NOT_CONFIGURED", "Server is not configured", details);
105698
105698
  }
105699
105699
  };
105700
+ var ContentTooLargeError = class extends ProtocolError {
105701
+ constructor(details) {
105702
+ super(413, "CONTENT_TOO_LARGE", "Content too large", details);
105703
+ }
105704
+ };
105700
105705
  var LineageInvalidError = class extends ProtocolError {
105701
105706
  constructor(message, details) {
105702
105707
  super(400, "LINEAGE_INVALID", message, details);
@@ -105747,6 +105752,31 @@ var DataDeletedError = class extends ProtocolError {
105747
105752
  super(410, "DATA_DELETED", "Data point has been deleted", details);
105748
105753
  }
105749
105754
  };
105755
+ var DerivativeQuestionInvalidError = class extends ProtocolError {
105756
+ constructor(message, details) {
105757
+ super(400, "DERIVATIVE_QUESTION_INVALID", message, details);
105758
+ }
105759
+ };
105760
+ var DerivativeQuestionNotFoundError = class extends ProtocolError {
105761
+ constructor(details) {
105762
+ super(404, "DERIVATIVE_QUESTION_NOT_FOUND", "Question not found", details);
105763
+ }
105764
+ };
105765
+ var DerivativeCycleError = class extends ProtocolError {
105766
+ constructor(details) {
105767
+ super(409, "DERIVATIVE_CYCLE", `Registering this question would make "${details.derivedScope}" a transitive source of itself; recompute would never settle`, details);
105768
+ }
105769
+ };
105770
+ var DerivativeSourceNotGrantedError = class extends ProtocolError {
105771
+ constructor(details) {
105772
+ super(403, "DERIVATIVE_SOURCE_NOT_GRANTED", "The builder's grant does not cover reading every source scope of this question", details);
105773
+ }
105774
+ };
105775
+ var DerivativeComputeUnavailableError = class extends ProtocolError {
105776
+ constructor(details) {
105777
+ super(503, "DERIVATIVE_COMPUTE_UNAVAILABLE", "This server has no derivative compute configured", details);
105778
+ }
105779
+ };
105750
105780
 
105751
105781
  // ../core/dist/sync/data-point-id.js
105752
105782
  function computeDataPointId(ownerAddress, scope) {
@@ -109719,6 +109749,17 @@ var DEFAULTS = {
109719
109749
  enabled: true,
109720
109750
  serverAddr: "frpc.server.vana.org",
109721
109751
  serverPort: 7e3
109752
+ },
109753
+ inference: {
109754
+ // OpenAI-compatible chat completions endpoint the derivative compute
109755
+ // layer calls. Point it at the Vana inference relay (which holds the
109756
+ // provider key) or straight at a provider for local development.
109757
+ baseUrl: "https://inference.phala.com/v1",
109758
+ model: "z-ai/glm-5.2",
109759
+ // Newest-first items kept per source scope when a prompt is assembled.
109760
+ maxSourceItems: 50,
109761
+ // Quiet period after a source scope changes before a recompute starts.
109762
+ recomputeDebounceMs: 5e3
109722
109763
  }
109723
109764
  };
109724
109765
  var StorageBackend = external_exports.enum([
@@ -109772,7 +109813,13 @@ var ServerConfigSchema = external_exports.object({
109772
109813
  enabled: external_exports.boolean().default(DEFAULTS.tunnel.enabled),
109773
109814
  serverAddr: external_exports.string().default(DEFAULTS.tunnel.serverAddr),
109774
109815
  serverPort: external_exports.number().int().min(1).max(65535).default(DEFAULTS.tunnel.serverPort)
109775
- }).default(DEFAULTS.tunnel)
109816
+ }).default(DEFAULTS.tunnel),
109817
+ inference: external_exports.object({
109818
+ baseUrl: external_exports.url().default(DEFAULTS.inference.baseUrl),
109819
+ model: external_exports.string().min(1).default(DEFAULTS.inference.model),
109820
+ maxSourceItems: external_exports.number().int().min(1).max(1e4).default(DEFAULTS.inference.maxSourceItems),
109821
+ recomputeDebounceMs: external_exports.number().int().min(0).max(36e5).default(DEFAULTS.inference.recomputeDebounceMs)
109822
+ }).default(DEFAULTS.inference)
109776
109823
  });
109777
109824
 
109778
109825
  // ../lite/dist/state.js
@@ -112676,6 +112723,18 @@ function apiLoggerAsLogger(logger) {
112676
112723
  error: (payload, message) => (logger?.error ?? noop)(payload, message ?? "")
112677
112724
  };
112678
112725
  }
112726
+ function notifyDataWritten(deps, event) {
112727
+ if (!deps.onDataWritten)
112728
+ return;
112729
+ try {
112730
+ deps.onDataWritten(event);
112731
+ } catch (err2) {
112732
+ deps.logger?.warn?.({
112733
+ scope: event.scope,
112734
+ error: err2 instanceof Error ? err2.message : String(err2)
112735
+ }, "onDataWritten hook failed; record already stored");
112736
+ }
112737
+ }
112679
112738
  function notifyNewData(syncManager) {
112680
112739
  if (!syncManager)
112681
112740
  return;
@@ -113040,6 +113099,11 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
113040
113099
  }, "Binary data file ingested");
113041
113100
  await logBuilderWrite();
113042
113101
  notifyNewData(deps.syncManager);
113102
+ notifyDataWritten(deps, {
113103
+ scope: scopeResult.scope,
113104
+ collectedAt: collectedAtValue,
113105
+ lineageSources: lineage2?.sources
113106
+ });
113043
113107
  return jsonResponse(result2.response, { status: 201 });
113044
113108
  }
113045
113109
  const parsed = await parseJsonObjectBody(request2, "Request body must be valid JSON");
@@ -113067,6 +113131,11 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
113067
113131
  }, "Data file ingested");
113068
113132
  await logBuilderWrite();
113069
113133
  notifyNewData(deps.syncManager);
113134
+ notifyDataWritten(deps, {
113135
+ scope: scopeResult.scope,
113136
+ collectedAt: collectedAtValue,
113137
+ lineageSources: lineage?.sources
113138
+ });
113070
113139
  return jsonResponse(result.response, { status: 201 });
113071
113140
  } catch (err2) {
113072
113141
  if (err2 instanceof IngestPersistedError) {
@@ -113293,6 +113362,93 @@ async function handlePersonalServerOauthTokenRequest(request2, deps) {
113293
113362
  });
113294
113363
  }
113295
113364
 
113365
+ // ../core/dist/derivatives/types.js
113366
+ function questionRegistrationView(registration) {
113367
+ return {
113368
+ questionId: registration.questionId,
113369
+ derivedScope: registration.derivedScope,
113370
+ sourceScopes: [...registration.sourceScopes],
113371
+ question: registration.question,
113372
+ model: registration.model,
113373
+ registeredBy: registration.registeredBy,
113374
+ status: registration.status,
113375
+ error: registration.error,
113376
+ createdAt: registration.createdAt,
113377
+ updatedAt: registration.updatedAt,
113378
+ lastComputedAt: registration.lastComputedAt,
113379
+ derivedVersion: registration.derivedVersion,
113380
+ derivedCollectedAt: registration.derivedCollectedAt
113381
+ };
113382
+ }
113383
+
113384
+ // ../core/dist/derivatives/store.js
113385
+ function clone2(registration) {
113386
+ return {
113387
+ ...registration,
113388
+ sourceScopes: [...registration.sourceScopes],
113389
+ registeredBy: { ...registration.registeredBy }
113390
+ };
113391
+ }
113392
+ function matchesQuestionFilter(registration, filter) {
113393
+ if (!filter)
113394
+ return true;
113395
+ if (filter.derivedScope && registration.derivedScope !== filter.derivedScope)
113396
+ return false;
113397
+ if (filter.sourceScope && !registration.sourceScopes.includes(filter.sourceScope))
113398
+ return false;
113399
+ if (filter.builder) {
113400
+ const by2 = registration.registeredBy;
113401
+ if (by2.kind !== "builder" || by2.builder.toLowerCase() !== filter.builder.toLowerCase())
113402
+ return false;
113403
+ }
113404
+ return true;
113405
+ }
113406
+ function sortQuestions(registrations) {
113407
+ return [...registrations].sort((a10, b10) => a10.createdAt.localeCompare(b10.createdAt) || a10.questionId.localeCompare(b10.questionId));
113408
+ }
113409
+ function createInMemoryQuestionStore(options = {}) {
113410
+ const byId = /* @__PURE__ */ new Map();
113411
+ for (const registration of options.initial ?? []) {
113412
+ byId.set(registration.questionId, clone2(registration));
113413
+ }
113414
+ async function changed() {
113415
+ if (!options.onChange)
113416
+ return;
113417
+ await options.onChange(sortQuestions([...byId.values()]).map(clone2));
113418
+ }
113419
+ return {
113420
+ async list(filter) {
113421
+ return sortQuestions([...byId.values()].filter((registration) => matchesQuestionFilter(registration, filter))).map(clone2);
113422
+ },
113423
+ async get(questionId) {
113424
+ const registration = byId.get(questionId);
113425
+ return registration ? clone2(registration) : null;
113426
+ },
113427
+ async insert(registration) {
113428
+ if (byId.has(registration.questionId)) {
113429
+ throw new Error(`Question ${registration.questionId} is already registered`);
113430
+ }
113431
+ byId.set(registration.questionId, clone2(registration));
113432
+ await changed();
113433
+ },
113434
+ async update(questionId, patch) {
113435
+ const current = byId.get(questionId);
113436
+ if (!current)
113437
+ return null;
113438
+ const next = { ...current, ...patch };
113439
+ byId.set(questionId, next);
113440
+ await changed();
113441
+ return clone2(next);
113442
+ },
113443
+ async delete(questionId) {
113444
+ const existed = byId.delete(questionId);
113445
+ if (existed)
113446
+ await changed();
113447
+ return existed;
113448
+ }
113449
+ };
113450
+ }
113451
+
113296
113452
  // ../core/dist/policy/data-read.js
113297
113453
  function parseGrantExpiresAtSeconds(value) {
113298
113454
  if (value === null || value === void 0 || value === "0")
@@ -113382,6 +113538,1084 @@ async function verifyDataReadPolicy(input, ports) {
113382
113538
  return grant;
113383
113539
  }
113384
113540
 
113541
+ // ../core/dist/policy/data-write.js
113542
+ var WRITE_SCOPE_PREFIX = "write:";
113543
+ function isWriteScopeEntry(entry) {
113544
+ return entry.startsWith(WRITE_SCOPE_PREFIX);
113545
+ }
113546
+ function writeScopePatterns(grantScopes) {
113547
+ return grantScopes.filter(isWriteScopeEntry).map((entry) => entry.slice(WRITE_SCOPE_PREFIX.length)).filter((pattern) => pattern.length > 0);
113548
+ }
113549
+ function scopeCoveredByWriteGrant(requestedScope, grantScopes) {
113550
+ return writeScopePatterns(grantScopes).some((pattern) => scopeMatchesPattern(requestedScope, pattern));
113551
+ }
113552
+ async function verifyDataWritePolicy(input, ports) {
113553
+ const available = await ports.runtimeAvailability?.isAvailable();
113554
+ if (available === false) {
113555
+ throw new PsUnavailableError();
113556
+ }
113557
+ const builder = await ports.authSessionVerifier.getBuilder(input.signer);
113558
+ if (!builder) {
113559
+ throw new UnregisteredBuilderError();
113560
+ }
113561
+ if (!input.grantId) {
113562
+ throw new GrantRequiredError({
113563
+ reason: "No grantId bound to the write session"
113564
+ });
113565
+ }
113566
+ const grant = await ports.grantVerifier.getGrant(input.grantId);
113567
+ if (!grant) {
113568
+ throw new GrantRequiredError({
113569
+ reason: "Grant not found",
113570
+ grantId: input.grantId
113571
+ });
113572
+ }
113573
+ if (grant.revokedAt !== null) {
113574
+ throw new GrantRevokedError({ grantId: grant.id });
113575
+ }
113576
+ if (!grant.scopes || writeScopePatterns(grant.scopes).length === 0) {
113577
+ throw new ScopeMismatchError({
113578
+ requestedScope: input.requestedScope,
113579
+ reason: "Grant has no write scopes"
113580
+ });
113581
+ }
113582
+ if (grant.expiresAt !== null && grant.expiresAt !== void 0) {
113583
+ const expiresAtSec = parseGrantExpiresAtSeconds(grant.expiresAt);
113584
+ if (expiresAtSec === null) {
113585
+ throw new ScopeMismatchError({
113586
+ requestedScope: input.requestedScope,
113587
+ reason: "Grant expiry is invalid"
113588
+ });
113589
+ }
113590
+ if (expiresAtSec > 0) {
113591
+ const nowSec = Math.floor(Date.now() / 1e3);
113592
+ if (expiresAtSec < nowSec) {
113593
+ throw new GrantExpiredError({
113594
+ expiresAt: expiresAtSec
113595
+ });
113596
+ }
113597
+ }
113598
+ }
113599
+ if (!scopeCoveredByWriteGrant(input.requestedScope, grant.scopes)) {
113600
+ throw new ScopeMismatchError({
113601
+ requestedScope: input.requestedScope,
113602
+ grantedScopes: grant.scopes,
113603
+ reason: "Grant does not authorize writing to this scope"
113604
+ });
113605
+ }
113606
+ if (builder.id.toLowerCase() !== grant.granteeId.toLowerCase()) {
113607
+ throw new InvalidSignatureError3({
113608
+ reason: "Write signer is not the grant builder",
113609
+ expected: grant.granteeId,
113610
+ actual: input.signer
113611
+ });
113612
+ }
113613
+ if (!input.serverOwner) {
113614
+ throw new ServerNotConfiguredError({
113615
+ reason: "serverOwner is required to verify grant ownership"
113616
+ });
113617
+ }
113618
+ if (!grant.grantorAddress || grant.grantorAddress.toLowerCase() !== input.serverOwner.toLowerCase()) {
113619
+ throw new GrantOwnerMismatchError({
113620
+ grantId: grant.id,
113621
+ expected: input.serverOwner,
113622
+ actual: grant.grantorAddress ?? null
113623
+ });
113624
+ }
113625
+ await ports.writeFeeVerifier?.assertWriteAllowed({
113626
+ builder: input.signer,
113627
+ grant,
113628
+ scope: input.requestedScope
113629
+ });
113630
+ return grant;
113631
+ }
113632
+
113633
+ // ../core/dist/derivatives/registration.js
113634
+ var MAX_QUESTION_SOURCE_SCOPES = 16;
113635
+ var MAX_QUESTION_CHARS = 8e3;
113636
+ var MAX_MODEL_CHARS = 128;
113637
+ var MAX_ECHOED_SCOPE_CHARS = 128;
113638
+ var MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
113639
+ function isRecord4(value) {
113640
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113641
+ }
113642
+ function parseScope(value, field) {
113643
+ if (typeof value !== "string") {
113644
+ throw new DerivativeQuestionInvalidError(`${field} must be a scope string`, {
113645
+ field
113646
+ });
113647
+ }
113648
+ const parsed = parseDataScopeContract(value);
113649
+ if (!parsed.ok) {
113650
+ throw new DerivativeQuestionInvalidError(`${field} is not a valid scope: ${parsed.body.message}`, { field, scope: value.slice(0, MAX_ECHOED_SCOPE_CHARS) });
113651
+ }
113652
+ return parsed.scope;
113653
+ }
113654
+ function parseQuestionInput(body) {
113655
+ if (!isRecord4(body)) {
113656
+ throw new DerivativeQuestionInvalidError("Body must be a JSON object");
113657
+ }
113658
+ const derivedScope = parseScope(body.derivedScope, "derivedScope");
113659
+ if (!Array.isArray(body.sourceScopes) || body.sourceScopes.length === 0) {
113660
+ throw new DerivativeQuestionInvalidError("sourceScopes must be a non-empty array of scopes", { field: "sourceScopes" });
113661
+ }
113662
+ if (body.sourceScopes.length > MAX_QUESTION_SOURCE_SCOPES) {
113663
+ throw new DerivativeQuestionInvalidError(`sourceScopes lists ${body.sourceScopes.length} scopes; the maximum is ${MAX_QUESTION_SOURCE_SCOPES}`, { field: "sourceScopes", max: MAX_QUESTION_SOURCE_SCOPES });
113664
+ }
113665
+ const sourceScopes = [];
113666
+ for (const entry of body.sourceScopes) {
113667
+ const scope = parseScope(entry, "sourceScopes[]");
113668
+ if (sourceScopes.includes(scope)) {
113669
+ throw new DerivativeQuestionInvalidError("sourceScopes lists the same scope twice", { field: "sourceScopes", duplicate: scope });
113670
+ }
113671
+ if (scope === derivedScope) {
113672
+ throw new DerivativeQuestionInvalidError("derivedScope cannot be one of its own sources", { field: "sourceScopes", scope });
113673
+ }
113674
+ sourceScopes.push(scope);
113675
+ }
113676
+ if (typeof body.question !== "string" || body.question.trim() === "") {
113677
+ throw new DerivativeQuestionInvalidError("question must be a non-empty string", { field: "question" });
113678
+ }
113679
+ if (body.question.length > MAX_QUESTION_CHARS) {
113680
+ throw new DerivativeQuestionInvalidError(`question is ${body.question.length} characters; the maximum is ${MAX_QUESTION_CHARS}`, { field: "question", max: MAX_QUESTION_CHARS });
113681
+ }
113682
+ let model = null;
113683
+ if (body.model !== void 0 && body.model !== null) {
113684
+ if (typeof body.model !== "string" || body.model.length > MAX_MODEL_CHARS || !MODEL_ID.test(body.model)) {
113685
+ throw new DerivativeQuestionInvalidError("model must be a provider model id", { field: "model" });
113686
+ }
113687
+ model = body.model;
113688
+ }
113689
+ assertDerivedScopeNaming(derivedScope, sourceScopes);
113690
+ return { derivedScope, sourceScopes, question: body.question, model };
113691
+ }
113692
+ function findDerivationCycle(candidate, existing) {
113693
+ const sourcesOf = /* @__PURE__ */ new Map();
113694
+ const add2 = (derived, sources) => {
113695
+ const set2 = sourcesOf.get(derived) ?? /* @__PURE__ */ new Set();
113696
+ for (const source of sources)
113697
+ set2.add(source);
113698
+ sourcesOf.set(derived, set2);
113699
+ };
113700
+ for (const registration of existing) {
113701
+ add2(registration.derivedScope, registration.sourceScopes);
113702
+ }
113703
+ add2(candidate.derivedScope, candidate.sourceScopes);
113704
+ const target = candidate.derivedScope;
113705
+ const visited = /* @__PURE__ */ new Set();
113706
+ const stack = [
113707
+ { scope: target, path: [target] }
113708
+ ];
113709
+ while (stack.length > 0) {
113710
+ const { scope, path } = stack.pop();
113711
+ for (const source of sourcesOf.get(scope) ?? []) {
113712
+ if (source === target)
113713
+ return [...path, source];
113714
+ if (visited.has(source))
113715
+ continue;
113716
+ visited.add(source);
113717
+ stack.push({ scope: source, path: [...path, source] });
113718
+ }
113719
+ }
113720
+ return null;
113721
+ }
113722
+ async function createQuestionRegistration(input) {
113723
+ const parsed = parseQuestionInput(input.body);
113724
+ const cycle = findDerivationCycle(parsed, await input.store.list());
113725
+ if (cycle) {
113726
+ throw new DerivativeCycleError({
113727
+ derivedScope: parsed.derivedScope,
113728
+ path: cycle
113729
+ });
113730
+ }
113731
+ const at3 = input.now().toISOString();
113732
+ const registration = {
113733
+ questionId: input.questionId,
113734
+ derivedScope: parsed.derivedScope,
113735
+ sourceScopes: parsed.sourceScopes,
113736
+ question: parsed.question,
113737
+ model: parsed.model,
113738
+ registeredBy: input.registeredBy,
113739
+ status: "pending",
113740
+ error: null,
113741
+ createdAt: at3,
113742
+ updatedAt: at3,
113743
+ lastComputedAt: null,
113744
+ derivedVersion: null,
113745
+ derivedCollectedAt: null
113746
+ };
113747
+ await input.store.insert(registration);
113748
+ return registration;
113749
+ }
113750
+ function uncoveredSourceScopes(sourceScopes, grantScopes) {
113751
+ const readEntries = (grantScopes ?? []).filter((entry) => !isWriteScopeEntry(entry));
113752
+ return sourceScopes.filter((scope) => !scopeCoveredByGrant(scope, readEntries));
113753
+ }
113754
+
113755
+ // ../core/dist/derivatives/prompt.js
113756
+ var DEFAULT_MAX_SOURCE_ITEMS = 50;
113757
+ var DEFAULT_MAX_SOURCE_CHARS = 2e5;
113758
+ var TIMESTAMP_KEYS = [
113759
+ "collectedAt",
113760
+ "updatedAt",
113761
+ "updated_at",
113762
+ "update_time",
113763
+ "createdAt",
113764
+ "created_at",
113765
+ "create_time",
113766
+ "timestamp",
113767
+ "time",
113768
+ "date",
113769
+ "publishedAt",
113770
+ "published_at"
113771
+ ];
113772
+ var RESERVED_KEYS = /* @__PURE__ */ new Set(["$lineage", "$writtenBy", "$binary"]);
113773
+ function isRecord5(value) {
113774
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113775
+ }
113776
+ function timestampOf(item) {
113777
+ if (!isRecord5(item))
113778
+ return null;
113779
+ for (const key of TIMESTAMP_KEYS) {
113780
+ const value = item[key];
113781
+ if (typeof value === "number" && Number.isFinite(value)) {
113782
+ return value < 1e12 ? value * 1e3 : value;
113783
+ }
113784
+ if (typeof value === "string") {
113785
+ const parsed = Date.parse(value);
113786
+ if (!Number.isNaN(parsed))
113787
+ return parsed;
113788
+ }
113789
+ }
113790
+ return null;
113791
+ }
113792
+ function sortNewestFirst(items) {
113793
+ const indexed = items.map((item, index) => ({
113794
+ item,
113795
+ index,
113796
+ at: timestampOf(item)
113797
+ }));
113798
+ const dated = indexed.filter((entry) => entry.at !== null).sort((a10, b10) => b10.at - a10.at || b10.index - a10.index);
113799
+ const undated = indexed.filter((entry) => entry.at === null).reverse();
113800
+ return [...dated, ...undated].map((entry) => entry.item);
113801
+ }
113802
+ function trimSourceData(data, options = {}) {
113803
+ const maxItems = Math.max(1, options.maxItems ?? DEFAULT_MAX_SOURCE_ITEMS);
113804
+ const maxChars = Math.max(1, options.maxChars ?? DEFAULT_MAX_SOURCE_CHARS);
113805
+ let kept = 0;
113806
+ let total = 0;
113807
+ const trimArray = (items, limit2) => {
113808
+ total += items.length;
113809
+ const sorted = sortNewestFirst(items).slice(0, limit2);
113810
+ kept += sorted.length;
113811
+ return sorted;
113812
+ };
113813
+ const build = (limit2) => {
113814
+ kept = 0;
113815
+ total = 0;
113816
+ if (Array.isArray(data))
113817
+ return trimArray(data, limit2);
113818
+ if (isRecord5(data)) {
113819
+ const out = {};
113820
+ for (const [key, value] of Object.entries(data)) {
113821
+ if (RESERVED_KEYS.has(key))
113822
+ continue;
113823
+ out[key] = Array.isArray(value) ? trimArray(value, limit2) : value;
113824
+ }
113825
+ return out;
113826
+ }
113827
+ return data;
113828
+ };
113829
+ let limit = maxItems;
113830
+ let result = build(limit);
113831
+ let text = JSON.stringify(result) ?? "null";
113832
+ while (text.length > maxChars && limit > 1) {
113833
+ limit = Math.max(1, Math.floor(limit / 2));
113834
+ result = build(limit);
113835
+ text = JSON.stringify(result) ?? "null";
113836
+ }
113837
+ if (text.length > maxChars) {
113838
+ return {
113839
+ data: `${text.slice(0, maxChars)}...[truncated]`,
113840
+ kept,
113841
+ total,
113842
+ truncated: true
113843
+ };
113844
+ }
113845
+ return { data: result, kept, total, truncated: false };
113846
+ }
113847
+ var SYSTEM_PROMPT = [
113848
+ "You answer a question about a person using ONLY the user data provided in the message.",
113849
+ "Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer.",
113850
+ "Respond with a single JSON object and nothing else, with exactly these fields:",
113851
+ ' "answer": string, the answer to the question, written for the person the data belongs to;',
113852
+ ' "evidence": string, a short summary of which parts of the data support the answer.'
113853
+ ].join("\n");
113854
+ function buildQuestionMessages(input) {
113855
+ const sections = input.sources.map((source) => {
113856
+ const note = source.total > source.kept ? ` (newest ${source.kept} of ${source.total} items)` : "";
113857
+ const cut = source.truncated ? " (truncated)" : "";
113858
+ return [
113859
+ `### Scope: ${source.scope}${note}${cut}`,
113860
+ `Collected at: ${source.collectedAt}`,
113861
+ JSON.stringify(source.data)
113862
+ ].join("\n");
113863
+ });
113864
+ const user = [
113865
+ "## Question",
113866
+ input.question,
113867
+ "",
113868
+ "## User data",
113869
+ ...sections,
113870
+ "",
113871
+ "Answer the question as a JSON object with the fields answer and evidence."
113872
+ ].join("\n");
113873
+ return [
113874
+ { role: "system", content: SYSTEM_PROMPT },
113875
+ { role: "user", content: user }
113876
+ ];
113877
+ }
113878
+ function parseAnswer(content) {
113879
+ const candidates = [content.trim()];
113880
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(content);
113881
+ if (fenced?.[1])
113882
+ candidates.unshift(fenced[1].trim());
113883
+ const first = content.indexOf("{");
113884
+ const last2 = content.lastIndexOf("}");
113885
+ if (first !== -1 && last2 > first) {
113886
+ candidates.push(content.slice(first, last2 + 1));
113887
+ }
113888
+ for (const candidate of candidates) {
113889
+ try {
113890
+ const parsed = JSON.parse(candidate);
113891
+ if (isRecord5(parsed) && typeof parsed.answer === "string") {
113892
+ return {
113893
+ answer: parsed.answer,
113894
+ evidence: typeof parsed.evidence === "string" ? parsed.evidence : null
113895
+ };
113896
+ }
113897
+ } catch {
113898
+ }
113899
+ }
113900
+ return { answer: content.trim(), evidence: null };
113901
+ }
113902
+
113903
+ // ../core/dist/derivatives/inference.js
113904
+ var DEFAULT_INFERENCE_BASE_URL = "https://inference.phala.com/v1";
113905
+ var DEFAULT_INFERENCE_MODEL = "z-ai/glm-5.2";
113906
+ var DEFAULT_INFERENCE_TIMEOUT_MS = 12e4;
113907
+ var DEFAULT_INFERENCE_MAX_TOKENS = 2048;
113908
+ var DEFAULT_INFERENCE_REQUEST_FIELDS = {
113909
+ provider: { aci_verified: true, zdr: true }
113910
+ };
113911
+ var InferenceRequestError = class extends Error {
113912
+ status;
113913
+ constructor(message, status2) {
113914
+ super(message);
113915
+ this.status = status2;
113916
+ this.name = "InferenceRequestError";
113917
+ }
113918
+ };
113919
+ function isRecord6(value) {
113920
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113921
+ }
113922
+ function readUsage(value) {
113923
+ if (!isRecord6(value))
113924
+ return void 0;
113925
+ const num2 = (v10) => typeof v10 === "number" ? v10 : void 0;
113926
+ const usage = {
113927
+ promptTokens: num2(value.prompt_tokens),
113928
+ completionTokens: num2(value.completion_tokens),
113929
+ totalTokens: num2(value.total_tokens)
113930
+ };
113931
+ return usage;
113932
+ }
113933
+ function readContent(body) {
113934
+ if (!isRecord6(body) || !Array.isArray(body.choices))
113935
+ return null;
113936
+ const first = body.choices[0];
113937
+ if (!isRecord6(first) || !isRecord6(first.message))
113938
+ return null;
113939
+ const content = first.message.content;
113940
+ if (typeof content === "string")
113941
+ return content;
113942
+ if (Array.isArray(content)) {
113943
+ const text = content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").join("");
113944
+ return text;
113945
+ }
113946
+ return null;
113947
+ }
113948
+ function createOpenAiCompatibleInferenceProvider(options = {}) {
113949
+ const base = (options.baseUrl ?? DEFAULT_INFERENCE_BASE_URL).replace(/\/+$/, "");
113950
+ const defaultModel = options.model ?? DEFAULT_INFERENCE_MODEL;
113951
+ const timeoutMs = options.timeoutMs ?? DEFAULT_INFERENCE_TIMEOUT_MS;
113952
+ const doFetch = options.fetch ?? fetch;
113953
+ const requestFields = options.requestFields ?? DEFAULT_INFERENCE_REQUEST_FIELDS;
113954
+ return {
113955
+ defaultModel,
113956
+ async chat(input) {
113957
+ const headers = new Headers({ "Content-Type": "application/json" });
113958
+ if (options.apiKey) {
113959
+ headers.set("Authorization", `Bearer ${options.apiKey}`);
113960
+ }
113961
+ let messages = input.messages;
113962
+ if (options.encryption) {
113963
+ ({ messages } = await options.encryption.encryptRequest({
113964
+ messages,
113965
+ headers
113966
+ }));
113967
+ }
113968
+ const body = {
113969
+ ...requestFields,
113970
+ model: input.model || defaultModel,
113971
+ messages,
113972
+ max_tokens: input.maxTokens ?? DEFAULT_INFERENCE_MAX_TOKENS
113973
+ };
113974
+ let response;
113975
+ try {
113976
+ response = await doFetch(`${base}/chat/completions`, {
113977
+ method: "POST",
113978
+ headers,
113979
+ body: JSON.stringify(body),
113980
+ signal: AbortSignal.timeout(timeoutMs)
113981
+ });
113982
+ } catch (err2) {
113983
+ const name = err2 instanceof Error ? err2.name : "Error";
113984
+ throw new InferenceRequestError(`inference request failed before a response (${name})`, null);
113985
+ }
113986
+ if (!response.ok) {
113987
+ throw new InferenceRequestError(`inference request failed with status ${response.status}`, response.status);
113988
+ }
113989
+ let parsed;
113990
+ try {
113991
+ parsed = await response.json();
113992
+ } catch {
113993
+ throw new InferenceRequestError("inference response was not JSON", response.status);
113994
+ }
113995
+ let content = readContent(parsed);
113996
+ if (content === null || content.trim() === "") {
113997
+ throw new InferenceRequestError("inference response carried no assistant content", response.status);
113998
+ }
113999
+ if (options.encryption) {
114000
+ content = await options.encryption.decryptResponse({
114001
+ content,
114002
+ headers: response.headers
114003
+ });
114004
+ }
114005
+ const receiptId = response.headers.get("x-receipt-id") ?? void 0;
114006
+ const aciIdentity = response.headers.get("x-aci-identity") ?? void 0;
114007
+ return {
114008
+ content,
114009
+ usage: readUsage(isRecord6(parsed) ? parsed.usage : void 0),
114010
+ ...receiptId ? { receiptId } : {},
114011
+ ...aciIdentity ? { aciIdentity } : {}
114012
+ };
114013
+ }
114014
+ };
114015
+ }
114016
+
114017
+ // ../core/dist/derivatives/compute.js
114018
+ var DEFAULT_RETRY_DELAYS_MS = [1e3, 4e3];
114019
+ function isRetryableInferenceError(err2) {
114020
+ return err2 instanceof InferenceRequestError && (err2.status === null || err2.status === 429 || err2.status >= 500);
114021
+ }
114022
+ async function withRetries(deps, attempt, retryable) {
114023
+ const delays = deps.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
114024
+ const sleep2 = deps.sleep ?? ((ms3) => new Promise((r10) => setTimeout(r10, ms3)));
114025
+ for (let index = 0; ; index += 1) {
114026
+ try {
114027
+ return await attempt();
114028
+ } catch (err2) {
114029
+ if (index >= delays.length || !retryable(err2))
114030
+ throw err2;
114031
+ await sleep2(delays[index]);
114032
+ }
114033
+ }
114034
+ }
114035
+ var ComputeFailure = class extends Error {
114036
+ constructor(message) {
114037
+ super(message);
114038
+ this.name = "ComputeFailure";
114039
+ }
114040
+ };
114041
+ function shortError(err2) {
114042
+ if (err2 instanceof ComputeFailure)
114043
+ return err2.message;
114044
+ if (err2 instanceof InferenceRequestError)
114045
+ return err2.message;
114046
+ if (err2 instanceof ProtocolError)
114047
+ return `${err2.errorCode}: ${err2.message}`;
114048
+ return `compute failed (${err2 instanceof Error ? err2.name : "Error"})`;
114049
+ }
114050
+ function collectedAtStamp(now, isTaken) {
114051
+ const base = now();
114052
+ base.setUTCMilliseconds(0);
114053
+ for (let bump = 0; bump < 60; bump += 1) {
114054
+ const candidate = new Date(base.getTime() + bump * 1e3).toISOString().replace(/\.\d{3}Z$/, "Z");
114055
+ if (!isTaken(candidate))
114056
+ return candidate;
114057
+ }
114058
+ throw new ComputeFailure("could not allocate a version stamp");
114059
+ }
114060
+ async function tombstoneMarker(scopeDeletions, scope) {
114061
+ if (!scopeDeletions)
114062
+ return null;
114063
+ const verdict = await scopeDeletions.resolve(scope);
114064
+ if (!verdict.deleted || verdict.version === null)
114065
+ return null;
114066
+ const version4 = Number(verdict.version);
114067
+ return Number.isSafeInteger(version4) ? version4 : null;
114068
+ }
114069
+ function localScopesById2(storage, serverOwner) {
114070
+ const byId = /* @__PURE__ */ new Map();
114071
+ for (let offset = 0; ; offset += LOCAL_SCOPE_SCAN_PAGE) {
114072
+ const { scopes, total } = storage.listScopes({
114073
+ limit: LOCAL_SCOPE_SCAN_PAGE,
114074
+ offset
114075
+ });
114076
+ for (const summary of scopes) {
114077
+ byId.set(computeDataPointId(serverOwner, summary.scope), summary.scope);
114078
+ }
114079
+ if (scopes.length === 0 || offset + scopes.length >= total)
114080
+ break;
114081
+ }
114082
+ return byId;
114083
+ }
114084
+ async function assertNoLineageCycle(deps, registration, serverOwner, sourceLineage) {
114085
+ const derivedId = computeDataPointId(serverOwner, registration.derivedScope);
114086
+ let byId = null;
114087
+ const visited = /* @__PURE__ */ new Set();
114088
+ const stack = [];
114089
+ for (const [scope, sources] of sourceLineage) {
114090
+ for (const id2 of sources)
114091
+ stack.push({ id: id2, path: [scope] });
114092
+ }
114093
+ while (stack.length > 0) {
114094
+ const { id: id2, path } = stack.pop();
114095
+ if (id2 === derivedId) {
114096
+ throw new DerivativeCycleError({
114097
+ derivedScope: registration.derivedScope,
114098
+ path: [registration.derivedScope, ...path, registration.derivedScope]
114099
+ });
114100
+ }
114101
+ if (visited.has(id2))
114102
+ continue;
114103
+ visited.add(id2);
114104
+ byId ??= localScopesById2(deps.storage, serverOwner);
114105
+ const scope = byId.get(id2);
114106
+ if (!scope)
114107
+ continue;
114108
+ const entry = deps.storage.findEntry({ scope });
114109
+ if (!entry)
114110
+ continue;
114111
+ let sources = [];
114112
+ try {
114113
+ const envelope = await deps.storage.readEnvelope(scope, entry.collectedAt);
114114
+ sources = readStoredLineage(envelope.data)?.sources ?? [];
114115
+ } catch {
114116
+ }
114117
+ for (const next of sources)
114118
+ stack.push({ id: next, path: [...path, scope] });
114119
+ }
114120
+ }
114121
+ async function loadSource(deps, scope) {
114122
+ const entry = deps.storage.findEntry({ scope });
114123
+ const deletion = await resolveReadDeletion({ scopeDeletions: deps.scopeDeletions, serverOwner: deps.serverOwner }, scope, entry);
114124
+ if (deletion) {
114125
+ throw new ComputeFailure(`source scope ${scope} is deleted`);
114126
+ }
114127
+ if (!entry) {
114128
+ throw new ComputeFailure(`source scope ${scope} has no local data`);
114129
+ }
114130
+ let envelope;
114131
+ try {
114132
+ envelope = await deps.storage.readEnvelope(scope, entry.collectedAt);
114133
+ } catch {
114134
+ throw new ComputeFailure(`source scope ${scope} could not be read`);
114135
+ }
114136
+ let lineageSources = [];
114137
+ try {
114138
+ lineageSources = readStoredLineage(envelope.data)?.sources ?? [];
114139
+ } catch {
114140
+ }
114141
+ const raw = isBinaryEnvelope(envelope) ? {
114142
+ binary: true,
114143
+ note: "binary record; its content is not included in the prompt"
114144
+ } : envelope.data;
114145
+ const trimmed = trimSourceData(raw, {
114146
+ maxItems: deps.maxSourceItems,
114147
+ maxChars: deps.maxSourceChars
114148
+ });
114149
+ return {
114150
+ source: {
114151
+ scope,
114152
+ collectedAt: entry.collectedAt,
114153
+ version: entry.version,
114154
+ data: trimmed.data,
114155
+ kept: trimmed.kept,
114156
+ total: trimmed.total,
114157
+ truncated: trimmed.truncated
114158
+ },
114159
+ lineageSources
114160
+ };
114161
+ }
114162
+ async function assertGrantStillValid(deps, registration) {
114163
+ if (registration.registeredBy.kind !== "builder")
114164
+ return;
114165
+ if (!deps.writePolicyPorts) {
114166
+ throw new ComputeFailure("builder grant verification is not configured");
114167
+ }
114168
+ if (!deps.serverOwner) {
114169
+ throw new ComputeFailure("server owner is not configured");
114170
+ }
114171
+ const { builder, grantId } = registration.registeredBy;
114172
+ const ports = deps.writePolicyPorts;
114173
+ const serverOwner = deps.serverOwner;
114174
+ const grant = await withRetries(deps, () => verifyDataWritePolicy({
114175
+ signer: builder,
114176
+ grantId,
114177
+ requestedScope: registration.derivedScope,
114178
+ serverOwner
114179
+ }, ports), (err2) => !(err2 instanceof ProtocolError));
114180
+ const uncovered = uncoveredSourceScopes(registration.sourceScopes, grant.scopes ?? []);
114181
+ if (uncovered.length > 0) {
114182
+ throw new DerivativeSourceNotGrantedError({ scopes: uncovered });
114183
+ }
114184
+ }
114185
+ async function computeQuestion(questionId, deps) {
114186
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
114187
+ if (await deps.runtimeAvailability?.isAvailable() === false) {
114188
+ return { status: "skipped", reason: "runtime-unavailable" };
114189
+ }
114190
+ const registration = await deps.store.get(questionId);
114191
+ if (!registration)
114192
+ return { status: "skipped", reason: "unknown-question" };
114193
+ try {
114194
+ if (!deps.serverOwner) {
114195
+ throw new ComputeFailure("server owner is not configured");
114196
+ }
114197
+ const serverOwner = deps.serverOwner;
114198
+ await assertGrantStillValid(deps, registration);
114199
+ assertDerivedScopeNaming(registration.derivedScope, registration.sourceScopes);
114200
+ const sources = [];
114201
+ const sourceLineage = /* @__PURE__ */ new Map();
114202
+ for (const scope of registration.sourceScopes) {
114203
+ const loaded = await loadSource(deps, scope);
114204
+ sources.push(loaded.source);
114205
+ sourceLineage.set(scope, loaded.lineageSources);
114206
+ }
114207
+ await assertNoLineageCycle(deps, registration, serverOwner, sourceLineage);
114208
+ const messages = buildQuestionMessages({
114209
+ question: registration.question,
114210
+ sources
114211
+ });
114212
+ const model = registration.model ?? deps.provider.defaultModel;
114213
+ const reply = await withRetries(deps, () => deps.provider.chat({ model, messages, maxTokens: deps.maxTokens }), isRetryableInferenceError);
114214
+ const parsed = parseAnswer(reply.content);
114215
+ const computedAt = now().toISOString();
114216
+ const lineageIds = registration.sourceScopes.map((scope) => computeDataPointId(serverOwner, scope));
114217
+ const record2 = {
114218
+ questionId: registration.questionId,
114219
+ question: registration.question,
114220
+ answer: parsed.answer,
114221
+ evidence: parsed.evidence,
114222
+ model,
114223
+ computedAt,
114224
+ sources: sources.map((source) => ({
114225
+ scope: source.scope,
114226
+ version: source.version,
114227
+ collectedAt: source.collectedAt
114228
+ })),
114229
+ lineage: lineageIds,
114230
+ ...reply.receiptId || reply.aciIdentity ? {
114231
+ inference: {
114232
+ ...reply.receiptId ? { receiptId: reply.receiptId } : {},
114233
+ ...reply.aciIdentity ? { aciIdentity: reply.aciIdentity } : {}
114234
+ }
114235
+ } : {}
114236
+ };
114237
+ const lineage = {
114238
+ sources: lineageIds,
114239
+ writtenAt: computedAt
114240
+ };
114241
+ const collectedAt2 = collectedAtStamp(now, (candidate) => deps.storage.findEntry({
114242
+ scope: registration.derivedScope,
114243
+ at: candidate
114244
+ })?.collectedAt === candidate);
114245
+ const written = await ingestDataContract({
114246
+ storage: deps.storage,
114247
+ scopeParam: registration.derivedScope,
114248
+ body: record2,
114249
+ collectedAt: collectedAt2,
114250
+ status: deps.syncManager ? "syncing" : "stored",
114251
+ lineage,
114252
+ afterTombstoneVersion: await tombstoneMarker(deps.scopeDeletions, registration.derivedScope)
114253
+ });
114254
+ if (!written.ok) {
114255
+ throw new ComputeFailure(`derived record rejected: ${written.body.error}`);
114256
+ }
114257
+ const entry = deps.storage.findEntry({
114258
+ scope: registration.derivedScope,
114259
+ at: collectedAt2
114260
+ });
114261
+ const updated = await deps.store.update(questionId, {
114262
+ status: "ready",
114263
+ error: null,
114264
+ updatedAt: computedAt,
114265
+ lastComputedAt: computedAt,
114266
+ derivedVersion: entry?.version ?? null,
114267
+ derivedCollectedAt: collectedAt2
114268
+ });
114269
+ if (deps.syncManager?.notifyNewData) {
114270
+ deps.syncManager.notifyNewData();
114271
+ } else if (deps.syncManager?.trigger) {
114272
+ void deps.syncManager.trigger().catch(() => void 0);
114273
+ }
114274
+ try {
114275
+ deps.onDerivedWritten?.({
114276
+ scope: registration.derivedScope,
114277
+ collectedAt: collectedAt2,
114278
+ lineageSources: lineageIds
114279
+ });
114280
+ } catch (err2) {
114281
+ deps.logger?.warn?.({
114282
+ questionId,
114283
+ derivedScope: registration.derivedScope,
114284
+ error: err2 instanceof Error ? err2.name : String(err2)
114285
+ }, "onDerivedWritten hook failed; derivative already written");
114286
+ }
114287
+ deps.logger?.info?.({
114288
+ questionId,
114289
+ derivedScope: registration.derivedScope,
114290
+ sourceScopes: registration.sourceScopes,
114291
+ model,
114292
+ version: entry?.version ?? null,
114293
+ receiptId: reply.receiptId ?? null
114294
+ }, "Derivative question computed");
114295
+ return {
114296
+ status: "ready",
114297
+ registration: updated ?? { ...registration, status: "ready" }
114298
+ };
114299
+ } catch (err2) {
114300
+ const error51 = shortError(err2);
114301
+ const at3 = now().toISOString();
114302
+ const updated = await deps.store.update(questionId, {
114303
+ status: "failed",
114304
+ error: error51,
114305
+ updatedAt: at3
114306
+ });
114307
+ deps.logger?.warn?.({ questionId, derivedScope: registration.derivedScope, error: error51 }, "Derivative question compute failed");
114308
+ return {
114309
+ status: "failed",
114310
+ registration: updated ?? { ...registration, status: "failed", error: error51 },
114311
+ error: error51
114312
+ };
114313
+ }
114314
+ }
114315
+
114316
+ // ../core/dist/derivatives/scheduler.js
114317
+ var defaultTimers = {
114318
+ setTimeout: (callback, ms3) => setTimeout(callback, ms3),
114319
+ clearTimeout: (handle) => clearTimeout(handle)
114320
+ };
114321
+ function createRecomputeScheduler(options) {
114322
+ const debounceMs = options.debounceMs ?? 5e3;
114323
+ const timers = options.timers ?? defaultTimers;
114324
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
114325
+ const states = /* @__PURE__ */ new Map();
114326
+ const pending = /* @__PURE__ */ new Set();
114327
+ let stopped = false;
114328
+ function track(promise2) {
114329
+ pending.add(promise2);
114330
+ const done = () => pending.delete(promise2);
114331
+ promise2.then(done, done);
114332
+ return promise2;
114333
+ }
114334
+ function warn(payload, message) {
114335
+ options.logger?.warn?.(payload, message);
114336
+ }
114337
+ function stateFor(questionId) {
114338
+ let state = states.get(questionId);
114339
+ if (!state) {
114340
+ state = { timer: null, running: null, rerun: false };
114341
+ states.set(questionId, state);
114342
+ }
114343
+ return state;
114344
+ }
114345
+ function run(questionId) {
114346
+ if (stopped)
114347
+ return;
114348
+ const state = stateFor(questionId);
114349
+ if (state.running) {
114350
+ state.rerun = true;
114351
+ return;
114352
+ }
114353
+ state.running = track(Promise.resolve().then(() => options.compute(questionId)).then(() => void 0, (err2) => warn({
114354
+ questionId,
114355
+ error: err2 instanceof Error ? err2.name : String(err2)
114356
+ }, "Derivative compute threw")).then(async () => {
114357
+ state.running = null;
114358
+ if (state.rerun) {
114359
+ state.rerun = false;
114360
+ await markStale(questionId);
114361
+ schedule(questionId, 0);
114362
+ } else if (!states.get(questionId)?.timer) {
114363
+ states.delete(questionId);
114364
+ }
114365
+ }));
114366
+ }
114367
+ function schedule(questionId, delayMs) {
114368
+ if (stopped)
114369
+ return;
114370
+ const state = stateFor(questionId);
114371
+ if (state.timer !== null)
114372
+ timers.clearTimeout(state.timer);
114373
+ state.timer = timers.setTimeout(() => {
114374
+ state.timer = null;
114375
+ run(questionId);
114376
+ }, delayMs);
114377
+ }
114378
+ async function markStale(questionId) {
114379
+ const registration = await options.store.get(questionId);
114380
+ if (!registration)
114381
+ return;
114382
+ if (registration.status === "ready" || registration.status === "failed") {
114383
+ await options.store.update(questionId, {
114384
+ status: "stale",
114385
+ updatedAt: now().toISOString()
114386
+ });
114387
+ }
114388
+ }
114389
+ return {
114390
+ markSourceChanged(scope, opts) {
114391
+ if (stopped)
114392
+ return;
114393
+ const lineage = new Set((opts?.lineageSources ?? []).map((id2) => id2.toLowerCase()));
114394
+ void track((async () => {
114395
+ const affected = await options.store.list({ sourceScope: scope });
114396
+ for (const registration of affected) {
114397
+ if (options.serverOwner && lineage.has(computeDataPointId(options.serverOwner, registration.derivedScope))) {
114398
+ continue;
114399
+ }
114400
+ await markStale(registration.questionId);
114401
+ schedule(registration.questionId, debounceMs);
114402
+ }
114403
+ })().catch((err2) => warn({ scope, error: err2 instanceof Error ? err2.name : String(err2) }, "Could not mark derivative questions stale")));
114404
+ },
114405
+ requestRecompute(questionId, opts) {
114406
+ if (stopped)
114407
+ return;
114408
+ void track(markStale(questionId).catch((err2) => warn({
114409
+ questionId,
114410
+ error: err2 instanceof Error ? err2.name : String(err2)
114411
+ }, "Could not mark derivative question stale")).then(() => schedule(questionId, opts?.immediate ? 0 : debounceMs)));
114412
+ },
114413
+ async whenIdle() {
114414
+ const waitForTimers = !options.timers;
114415
+ for (; ; ) {
114416
+ while (pending.size > 0) {
114417
+ await Promise.allSettled([...pending]);
114418
+ }
114419
+ const busy = [...states.values()].some((state) => state.running !== null || waitForTimers && state.timer !== null);
114420
+ if (!busy)
114421
+ return;
114422
+ await new Promise((resolve) => setTimeout(resolve, 5));
114423
+ }
114424
+ },
114425
+ stop() {
114426
+ stopped = true;
114427
+ for (const state of states.values()) {
114428
+ if (state.timer !== null)
114429
+ timers.clearTimeout(state.timer);
114430
+ state.timer = null;
114431
+ }
114432
+ },
114433
+ start() {
114434
+ if (!stopped)
114435
+ return;
114436
+ stopped = false;
114437
+ void track((async () => {
114438
+ for (const registration of await options.store.list()) {
114439
+ if (registration.status === "pending" || registration.status === "stale") {
114440
+ schedule(registration.questionId, 0);
114441
+ }
114442
+ }
114443
+ })().catch((err2) => warn({ error: err2 instanceof Error ? err2.name : String(err2) }, "Could not reschedule derivative questions")));
114444
+ }
114445
+ };
114446
+ }
114447
+
114448
+ // ../core/dist/derivatives/api.js
114449
+ var MAX_QUESTION_BODY_BYTES = 16 * 1024;
114450
+ function jsonResponse2(body, init) {
114451
+ const headers = new Headers(init?.headers);
114452
+ headers.set("Content-Type", "application/json");
114453
+ return new Response(JSON.stringify(body), { ...init, headers });
114454
+ }
114455
+ function errorResponse2(status2, errorCode, message) {
114456
+ return jsonResponse2({ error: { code: status2, errorCode, message } }, { status: status2 });
114457
+ }
114458
+ function stripBasePath2(pathname, basePath) {
114459
+ if (!basePath || basePath === "/")
114460
+ return pathname;
114461
+ if (pathname === basePath)
114462
+ return "/";
114463
+ if (pathname.startsWith(`${basePath}/`))
114464
+ return pathname.slice(basePath.length);
114465
+ return pathname;
114466
+ }
114467
+ async function authorizeOwnerOrWriter(deps, request2, scope) {
114468
+ if (deps.auth.authorizeWrite) {
114469
+ return await deps.auth.authorizeWrite({ request: request2, scope }) ?? void 0;
114470
+ }
114471
+ await deps.auth.authorizeOwner(request2);
114472
+ return void 0;
114473
+ }
114474
+ function sameBuilder(a10, b10) {
114475
+ return a10.toLowerCase() === b10.toLowerCase();
114476
+ }
114477
+ async function loadForCaller(deps, request2, store, questionId) {
114478
+ const registration = await store.get(questionId);
114479
+ if (!registration) {
114480
+ await deps.auth.authorizeOwner(request2);
114481
+ throw new DerivativeQuestionNotFoundError({ questionId });
114482
+ }
114483
+ const writer = await authorizeOwnerOrWriter(deps, request2, registration.derivedScope);
114484
+ if (writer) {
114485
+ const by2 = registration.registeredBy;
114486
+ if (by2.kind !== "builder" || !sameBuilder(by2.builder, writer.builder)) {
114487
+ await writer.releaseProof?.();
114488
+ throw new DerivativeQuestionNotFoundError({ questionId });
114489
+ }
114490
+ }
114491
+ return { registration, writer };
114492
+ }
114493
+ async function handlePersonalServerDerivativesRequest(request2, deps, options = {}) {
114494
+ try {
114495
+ const url2 = new URL(request2.url);
114496
+ const pathname = stripBasePath2(url2.pathname, options.basePath);
114497
+ const parts = pathname.split("/").filter(Boolean);
114498
+ if (parts[0] !== "questions" || parts.length > 3) {
114499
+ return errorResponse2(404, "NOT_FOUND", "Not found");
114500
+ }
114501
+ const compute = deps.compute;
114502
+ if (!compute)
114503
+ throw new DerivativeComputeUnavailableError();
114504
+ const { store, scheduler } = compute;
114505
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
114506
+ if (parts.length === 1) {
114507
+ if (request2.method === "GET") {
114508
+ const derivedScope = url2.searchParams.get("derivedScope") ?? void 0;
114509
+ if (derivedScope) {
114510
+ const writer = await authorizeOwnerOrWriter(deps, request2, derivedScope);
114511
+ const registrations2 = await store.list({
114512
+ derivedScope,
114513
+ ...writer ? { builder: writer.builder } : {}
114514
+ });
114515
+ return jsonResponse2({
114516
+ questions: registrations2.map(questionRegistrationView)
114517
+ });
114518
+ }
114519
+ await deps.auth.authorizeOwner(request2);
114520
+ const registrations = await store.list();
114521
+ return jsonResponse2({
114522
+ questions: registrations.map(questionRegistrationView)
114523
+ });
114524
+ }
114525
+ if (request2.method === "POST") {
114526
+ const declared = Number(request2.headers.get("content-length") ?? "0");
114527
+ if (declared > MAX_QUESTION_BODY_BYTES) {
114528
+ throw new ContentTooLargeError({ max: MAX_QUESTION_BODY_BYTES });
114529
+ }
114530
+ const bodyBytes = new Uint8Array(await request2.clone().arrayBuffer());
114531
+ if (bodyBytes.byteLength > MAX_QUESTION_BODY_BYTES) {
114532
+ throw new ContentTooLargeError({ max: MAX_QUESTION_BODY_BYTES });
114533
+ }
114534
+ const parsed = await parseJsonObjectBody(request2.clone(), "Request body must be valid JSON");
114535
+ if (!parsed.ok) {
114536
+ return jsonResponse2(parsed.result.body, {
114537
+ status: parsed.result.status
114538
+ });
114539
+ }
114540
+ const rawScope = parsed.body.derivedScope;
114541
+ const scopeForAuth = typeof rawScope === "string" ? rawScope : "";
114542
+ const writer = await authorizeOwnerOrWriter(deps, request2, scopeForAuth);
114543
+ const registeredBy = writer ? {
114544
+ kind: "builder",
114545
+ builder: writer.builder,
114546
+ grantId: writer.grantId
114547
+ } : { kind: "owner" };
114548
+ let registration;
114549
+ try {
114550
+ if (writer) {
114551
+ const input = parseQuestionInput(parsed.body);
114552
+ const uncovered = uncoveredSourceScopes(input.sourceScopes, writer.grantScopes);
114553
+ if (uncovered.length > 0) {
114554
+ throw new DerivativeSourceNotGrantedError({ scopes: uncovered });
114555
+ }
114556
+ }
114557
+ registration = await createQuestionRegistration({
114558
+ body: parsed.body,
114559
+ registeredBy,
114560
+ store,
114561
+ questionId: deps.createQuestionId?.() ?? crypto.randomUUID(),
114562
+ now
114563
+ });
114564
+ } catch (err2) {
114565
+ await writer?.releaseProof?.();
114566
+ throw err2;
114567
+ }
114568
+ deps.logger?.info?.({
114569
+ questionId: registration.questionId,
114570
+ derivedScope: registration.derivedScope,
114571
+ sourceScopes: registration.sourceScopes,
114572
+ registeredBy: registeredBy.kind,
114573
+ ...writer ? { builder: writer.builder, grantId: writer.grantId } : {}
114574
+ }, "Derivative question registered");
114575
+ scheduler.requestRecompute(registration.questionId, {
114576
+ immediate: true
114577
+ });
114578
+ return jsonResponse2(questionRegistrationView(registration), {
114579
+ status: 201
114580
+ });
114581
+ }
114582
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
114583
+ }
114584
+ const questionId = decodeURIComponent(parts[1] ?? "");
114585
+ if (parts.length === 3) {
114586
+ if (parts[2] !== "recompute") {
114587
+ return errorResponse2(404, "NOT_FOUND", "Not found");
114588
+ }
114589
+ if (request2.method !== "POST") {
114590
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
114591
+ }
114592
+ const { registration } = await loadForCaller(deps, request2, store, questionId);
114593
+ scheduler.requestRecompute(questionId, { immediate: true });
114594
+ return jsonResponse2({
114595
+ questionId,
114596
+ status: registration.status === "pending" ? "pending" : "stale",
114597
+ derivedScope: registration.derivedScope
114598
+ }, { status: 202 });
114599
+ }
114600
+ if (request2.method === "GET") {
114601
+ const { registration } = await loadForCaller(deps, request2, store, questionId);
114602
+ return jsonResponse2(questionRegistrationView(registration));
114603
+ }
114604
+ if (request2.method === "DELETE") {
114605
+ const { registration } = await loadForCaller(deps, request2, store, questionId);
114606
+ await store.delete(questionId);
114607
+ deps.logger?.info?.({ questionId, derivedScope: registration.derivedScope }, "Derivative question deleted");
114608
+ return jsonResponse2({ questionId, deleted: true });
114609
+ }
114610
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
114611
+ } catch (err2) {
114612
+ if (err2 instanceof ProtocolError) {
114613
+ return jsonResponse2(err2.toJSON(), { status: err2.code });
114614
+ }
114615
+ return errorResponse2(500, "INTERNAL_ERROR", "Internal server error");
114616
+ }
114617
+ }
114618
+
113385
114619
  // ../core/dist/mcp/store.js
113386
114620
  function createInMemoryMcpOAuthAuthorizationStore() {
113387
114621
  const byId = /* @__PURE__ */ new Map();
@@ -124090,7 +125324,7 @@ var Protocol = class {
124090
125324
  const capturedTransport = this._transport;
124091
125325
  const relatedTaskId = request2.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
124092
125326
  if (handler === void 0) {
124093
- const errorResponse3 = {
125327
+ const errorResponse4 = {
124094
125328
  jsonrpc: "2.0",
124095
125329
  id: request2.id,
124096
125330
  error: {
@@ -124101,11 +125335,11 @@ var Protocol = class {
124101
125335
  if (relatedTaskId && this._taskMessageQueue) {
124102
125336
  this._enqueueTaskMessage(relatedTaskId, {
124103
125337
  type: "error",
124104
- message: errorResponse3,
125338
+ message: errorResponse4,
124105
125339
  timestamp: Date.now()
124106
125340
  }, capturedTransport?.sessionId).catch((error51) => this._onerror(new Error(`Failed to enqueue error response: ${error51}`)));
124107
125341
  } else {
124108
- capturedTransport?.send(errorResponse3).catch((error51) => this._onerror(new Error(`Failed to send an error response: ${error51}`)));
125342
+ capturedTransport?.send(errorResponse4).catch((error51) => this._onerror(new Error(`Failed to send an error response: ${error51}`)));
124109
125343
  }
124110
125344
  return;
124111
125345
  }
@@ -124175,7 +125409,7 @@ var Protocol = class {
124175
125409
  if (abortController.signal.aborted) {
124176
125410
  return;
124177
125411
  }
124178
- const errorResponse3 = {
125412
+ const errorResponse4 = {
124179
125413
  jsonrpc: "2.0",
124180
125414
  id: request2.id,
124181
125415
  error: {
@@ -124187,11 +125421,11 @@ var Protocol = class {
124187
125421
  if (relatedTaskId && this._taskMessageQueue) {
124188
125422
  await this._enqueueTaskMessage(relatedTaskId, {
124189
125423
  type: "error",
124190
- message: errorResponse3,
125424
+ message: errorResponse4,
124191
125425
  timestamp: Date.now()
124192
125426
  }, capturedTransport?.sessionId);
124193
125427
  } else {
124194
- await capturedTransport?.send(errorResponse3);
125428
+ await capturedTransport?.send(errorResponse4);
124195
125429
  }
124196
125430
  }).catch((error51) => this._onerror(new Error(`Failed to send response: ${error51}`))).finally(() => {
124197
125431
  if (this._requestHandlerAbortControllers.get(request2.id) === abortController) {
@@ -127814,16 +129048,16 @@ async function collectDiagnosticsWithTimeout(recorder, options, timeoutMs = DIAG
127814
129048
  }
127815
129049
 
127816
129050
  // ../lite/dist/runtime.js
127817
- function jsonResponse2(body, init) {
129051
+ function jsonResponse3(body, init) {
127818
129052
  const headers = new Headers(init?.headers);
127819
129053
  headers.set("Content-Type", "application/json");
127820
129054
  return new Response(JSON.stringify(body), { ...init, headers });
127821
129055
  }
127822
129056
  function protocolErrorResponse2(err2) {
127823
- return jsonResponse2(err2.toJSON(), { status: err2.code });
129057
+ return jsonResponse3(err2.toJSON(), { status: err2.code });
127824
129058
  }
127825
- function errorResponse2(status2, errorCode, message) {
127826
- return jsonResponse2({
129059
+ function errorResponse3(status2, errorCode, message) {
129060
+ return jsonResponse3({
127827
129061
  error: {
127828
129062
  code: status2,
127829
129063
  errorCode,
@@ -127832,7 +129066,7 @@ function errorResponse2(status2, errorCode, message) {
127832
129066
  }, { status: status2 });
127833
129067
  }
127834
129068
  function mcpUnauthorized(origin, message = "MCP authorization required") {
127835
- return jsonResponse2({
129069
+ return jsonResponse3({
127836
129070
  error: {
127837
129071
  code: 401,
127838
129072
  errorCode: "MCP_AUTH_REQUIRED",
@@ -127881,7 +129115,7 @@ function redirectWithOAuthError(redirectUri, error51, description, state) {
127881
129115
  url2.searchParams.set("state", state);
127882
129116
  return Response.redirect(url2.toString(), 302);
127883
129117
  } catch {
127884
- return jsonResponse2({
129118
+ return jsonResponse3({
127885
129119
  error: error51,
127886
129120
  error_description: description,
127887
129121
  ...state ? { state } : {}
@@ -128087,11 +129321,11 @@ function createPsLiteRuntime(options) {
128087
129321
  if (err2 instanceof ProtocolError) {
128088
129322
  return protocolErrorResponse2(err2);
128089
129323
  }
128090
- return errorResponse2(500, "INTERNAL_ERROR", "Internal server error");
129324
+ return errorResponse3(500, "INTERNAL_ERROR", "Internal server error");
128091
129325
  }
128092
129326
  }
128093
129327
  function sendContractResult(result) {
128094
- return jsonResponse2(result.body, { status: result.status });
129328
+ return jsonResponse3(result.body, { status: result.status });
128095
129329
  }
128096
129330
  function ownerAddress() {
128097
129331
  return options.serverOwner ?? options.identity?.address;
@@ -128099,7 +129333,7 @@ function createPsLiteRuntime(options) {
128099
129333
  async function handleAuthDevice(request2, url2) {
128100
129334
  if (url2.pathname === "/auth/device") {
128101
129335
  if (request2.method !== "POST") {
128102
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129336
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128103
129337
  }
128104
129338
  return sendContractResult(initiateDeviceSessionContract({
128105
129339
  sessionStore: deviceSessions,
@@ -128113,7 +129347,7 @@ function createPsLiteRuntime(options) {
128113
129347
  }
128114
129348
  if (url2.pathname === "/auth/device/poll") {
128115
129349
  if (request2.method !== "GET") {
128116
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129350
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128117
129351
  }
128118
129352
  return sendContractResult(pollDeviceSessionContract({
128119
129353
  sessionStore: deviceSessions,
@@ -128125,11 +129359,11 @@ function createPsLiteRuntime(options) {
128125
129359
  if (url2.pathname === "/auth/device/approve") {
128126
129360
  const sessionId = url2.searchParams.get("session");
128127
129361
  if (!sessionId) {
128128
- return request2.method === "GET" ? new Response("Missing session parameter", { status: 400 }) : jsonResponse2({ error: { code: 400, message: "Missing session parameter" } }, { status: 400 });
129362
+ return request2.method === "GET" ? new Response("Missing session parameter", { status: 400 }) : jsonResponse3({ error: { code: 400, message: "Missing session parameter" } }, { status: 400 });
128129
129363
  }
128130
129364
  const session = deviceSessions.get(sessionId);
128131
129365
  if (!session) {
128132
- return request2.method === "GET" ? new Response("Session expired or invalid", { status: 404 }) : jsonResponse2({ error: { code: 404, message: "Session expired or invalid" } }, { status: 404 });
129366
+ return request2.method === "GET" ? new Response("Session expired or invalid", { status: 404 }) : jsonResponse3({ error: { code: 404, message: "Session expired or invalid" } }, { status: 404 });
128133
129367
  }
128134
129368
  if (request2.method === "GET") {
128135
129369
  return new Response("Device authorization pending", {
@@ -128137,10 +129371,10 @@ function createPsLiteRuntime(options) {
128137
129371
  });
128138
129372
  }
128139
129373
  if (request2.method !== "POST") {
128140
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129374
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128141
129375
  }
128142
129376
  if (session.status === "approved") {
128143
- return jsonResponse2({ status: "already_approved" });
129377
+ return jsonResponse3({ status: "already_approved" });
128144
129378
  }
128145
129379
  await auth.authorizeOwner(request2);
128146
129380
  return sendContractResult(await approveDeviceSessionContract({
@@ -128156,7 +129390,7 @@ function createPsLiteRuntime(options) {
128156
129390
  if (request2.method === "DELETE") {
128157
129391
  const token2 = bearerToken(request2);
128158
129392
  if (!token2) {
128159
- return jsonResponse2({ error: { code: 401, message: "Missing Bearer token" } }, { status: 401 });
129393
+ return jsonResponse3({ error: { code: 401, message: "Missing Bearer token" } }, { status: 401 });
128160
129394
  }
128161
129395
  return sendContractResult(await revokeDeviceTokenContract({
128162
129396
  tokenStore,
@@ -128164,11 +129398,11 @@ function createPsLiteRuntime(options) {
128164
129398
  }));
128165
129399
  }
128166
129400
  if (request2.method !== "POST") {
128167
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129401
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128168
129402
  }
128169
129403
  const token = bearerToken(request2);
128170
129404
  if (!options.accessToken || token !== options.accessToken) {
128171
- return jsonResponse2({
129405
+ return jsonResponse3({
128172
129406
  error: {
128173
129407
  code: 403,
128174
129408
  message: "Only control-plane tokens can provision Personal Server session tokens"
@@ -128179,10 +129413,10 @@ function createPsLiteRuntime(options) {
128179
129413
  try {
128180
129414
  body = await request2.json();
128181
129415
  } catch {
128182
- return jsonResponse2({ error: { code: 400, message: "Request body must be valid JSON" } }, { status: 400 });
129416
+ return jsonResponse3({ error: { code: 400, message: "Request body must be valid JSON" } }, { status: 400 });
128183
129417
  }
128184
129418
  if (!body.token || typeof body.token !== "string") {
128185
- return jsonResponse2({ error: { code: 400, message: "Missing token" } }, { status: 400 });
129419
+ return jsonResponse3({ error: { code: 400, message: "Missing token" } }, { status: 400 });
128186
129420
  }
128187
129421
  return sendContractResult(await provisionDeviceTokenContract({
128188
129422
  tokenStore,
@@ -128198,10 +129432,12 @@ function createPsLiteRuntime(options) {
128198
129432
  activate() {
128199
129433
  active = true;
128200
129434
  options.syncManager?.start?.();
129435
+ options.derivatives?.scheduler.start();
128201
129436
  },
128202
129437
  deactivate() {
128203
129438
  active = false;
128204
129439
  void options.syncManager?.stop?.();
129440
+ options.derivatives?.scheduler.stop();
128205
129441
  },
128206
129442
  isAvailable() {
128207
129443
  return active;
@@ -128235,7 +129471,7 @@ function createPsLiteRuntime(options) {
128235
129471
  accessLogs: accessLogReader.capabilities?.accessLogs ?? "custom",
128236
129472
  config: options.stateCapabilities?.config ?? (options.saveConfig ? "custom" : "indexeddb")
128237
129473
  };
128238
- return jsonResponse2({
129474
+ return jsonResponse3({
128239
129475
  status: active ? "healthy" : "unavailable",
128240
129476
  runtime: "ps-lite",
128241
129477
  storage: options.storage.kind,
@@ -128253,7 +129489,7 @@ function createPsLiteRuntime(options) {
128253
129489
  }
128254
129490
  if (url2.pathname === "/v1/diagnostics") {
128255
129491
  if (request2.method !== "GET") {
128256
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129492
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128257
129493
  }
128258
129494
  try {
128259
129495
  await auth.authorizeOwner(request2);
@@ -128264,7 +129500,7 @@ function createPsLiteRuntime(options) {
128264
129500
  throw err2;
128265
129501
  }
128266
129502
  if (!options.diagnostics) {
128267
- return jsonResponse2({
129503
+ return jsonResponse3({
128268
129504
  error: {
128269
129505
  code: 404,
128270
129506
  errorCode: "DIAGNOSTICS_NOT_CONFIGURED",
@@ -128278,7 +129514,7 @@ function createPsLiteRuntime(options) {
128278
129514
  syncStatus: syncStatus2,
128279
129515
  storage: dataStorage
128280
129516
  });
128281
- return jsonResponse2(snapshot);
129517
+ return jsonResponse3(snapshot);
128282
129518
  }
128283
129519
  if (!active) {
128284
129520
  return unavailableResponse();
@@ -128313,9 +129549,16 @@ function createPsLiteRuntime(options) {
128313
129549
  // serverOwner is absent the accessRecord is safely omitted.
128314
129550
  serverOwner: options.serverOwner,
128315
129551
  serverSigner: x402ServerSigner,
128316
- lineageGateway: options.lineageGateway
129552
+ lineageGateway: options.lineageGateway,
129553
+ // Recompute on refresh: a new local version marks every
129554
+ // question that reads the scope stale.
129555
+ onDataWritten: options.derivatives ? (event) => options.derivatives?.scheduler.markSourceChanged(event.scope, { lineageSources: event.lineageSources }) : void 0
128317
129556
  }, { basePath: dataPrefix });
128318
129557
  }
129558
+ const derivativesPrefix = "/v1/derivatives";
129559
+ if (url2.pathname.startsWith(`${derivativesPrefix}/`)) {
129560
+ return handlePersonalServerDerivativesRequest(request2, { auth, compute: options.derivatives ?? null, now }, { basePath: derivativesPrefix });
129561
+ }
128319
129562
  if (url2.pathname.startsWith("/auth/device")) {
128320
129563
  const response = await handleAuthDevice(request2, url2);
128321
129564
  if (response)
@@ -128404,7 +129647,7 @@ function createPsLiteRuntime(options) {
128404
129647
  if (mcpResponse)
128405
129648
  return mcpResponse;
128406
129649
  }
128407
- return errorResponse2(404, "NOT_FOUND", "Not found");
129650
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128408
129651
  });
128409
129652
  }
128410
129653
  };
@@ -128415,22 +129658,22 @@ async function handleMcpRoute(input) {
128415
129658
  const ownerAuthorizationPrefix = "/v1/mcp/oauth/authorizations";
128416
129659
  if (pathname === "/.well-known/oauth-protected-resource/mcp") {
128417
129660
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
128418
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129661
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
128419
129662
  }
128420
- return jsonResponse2(protectedResourceMetadata(input.serverOrigin));
129663
+ return jsonResponse3(protectedResourceMetadata(input.serverOrigin));
128421
129664
  }
128422
129665
  if (pathname === "/.well-known/oauth-authorization-server") {
128423
129666
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
128424
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129667
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
128425
129668
  }
128426
- return jsonResponse2(authorizationServerMetadata(input.serverOrigin));
129669
+ return jsonResponse3(authorizationServerMetadata(input.serverOrigin));
128427
129670
  }
128428
129671
  if (pathname === "/mcp/oauth/register") {
128429
129672
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
128430
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129673
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
128431
129674
  }
128432
129675
  if (input.request.method !== "POST") {
128433
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129676
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128434
129677
  }
128435
129678
  let body = {};
128436
129679
  try {
@@ -128438,7 +129681,7 @@ async function handleMcpRoute(input) {
128438
129681
  } catch {
128439
129682
  body = {};
128440
129683
  }
128441
- return jsonResponse2({
129684
+ return jsonResponse3({
128442
129685
  client_id: `mcp-client-${crypto.randomUUID()}`,
128443
129686
  client_name: body.client_name ?? "Claude",
128444
129687
  redirect_uris: Array.isArray(body.redirect_uris) ? body.redirect_uris : [],
@@ -128449,10 +129692,10 @@ async function handleMcpRoute(input) {
128449
129692
  }
128450
129693
  if (pathname === "/mcp/oauth/authorize") {
128451
129694
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
128452
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129695
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
128453
129696
  }
128454
129697
  if (input.request.method !== "GET") {
128455
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129698
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128456
129699
  }
128457
129700
  const responseType = input.url.searchParams.get("response_type");
128458
129701
  const clientId = input.url.searchParams.get("client_id") ?? "";
@@ -128480,7 +129723,7 @@ async function handleMcpRoute(input) {
128480
129723
  });
128481
129724
  const approvalUrl = resolveMcpApprovalUrl(input.approvalUrl);
128482
129725
  if (!approvalUrl) {
128483
- return errorResponse2(500, "MCP_APPROVAL_URL_MISSING", "MCP OAuth approval URL is not configured");
129726
+ return errorResponse3(500, "MCP_APPROVAL_URL_MISSING", "MCP OAuth approval URL is not configured");
128484
129727
  }
128485
129728
  const approve = new URL(approvalUrl);
128486
129729
  approve.searchParams.set("mcp_authorization", created.authorizationId);
@@ -128493,14 +129736,14 @@ async function handleMcpRoute(input) {
128493
129736
  }
128494
129737
  if (pathname === "/mcp/oauth/token") {
128495
129738
  if (!resolveMcpApprovalUrl(input.approvalUrl)) {
128496
- return errorResponse2(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
129739
+ return errorResponse3(404, "MCP_OAUTH_NOT_CONFIGURED", "MCP OAuth is not configured");
128497
129740
  }
128498
129741
  if (input.request.method !== "POST") {
128499
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129742
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128500
129743
  }
128501
129744
  const body = await parseFormBody(input.request);
128502
129745
  if (body.get("grant_type") !== "authorization_code") {
128503
- return jsonResponse2({
129746
+ return jsonResponse3({
128504
129747
  error: "unsupported_grant_type",
128505
129748
  error_description: "Only authorization_code is supported"
128506
129749
  }, { status: 400 });
@@ -128516,13 +129759,13 @@ async function handleMcpRoute(input) {
128516
129759
  connectionStore: input.store,
128517
129760
  now: input.now
128518
129761
  });
128519
- return jsonResponse2({
129762
+ return jsonResponse3({
128520
129763
  access_token: token.accessToken,
128521
129764
  token_type: "Bearer",
128522
129765
  ...token.scope ? { scope: token.scope } : {}
128523
129766
  });
128524
129767
  } catch (err2) {
128525
- return jsonResponse2({
129768
+ return jsonResponse3({
128526
129769
  error: err2 instanceof McpOAuthAuthorizationError ? err2.code : "invalid_grant",
128527
129770
  error_description: err2 instanceof Error ? err2.message : String(err2)
128528
129771
  }, { status: 400 });
@@ -128540,28 +129783,28 @@ async function handleMcpRoute(input) {
128540
129783
  const tail = pathname.slice(ownerAuthorizationPrefix.length + 1);
128541
129784
  const [id2, action] = tail.split("/");
128542
129785
  if (!id2) {
128543
- return errorResponse2(404, "NOT_FOUND", "Not found");
129786
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128544
129787
  }
128545
129788
  if (!action && input.request.method === "GET") {
128546
129789
  const record2 = await input.authorizationStore.getById(id2);
128547
129790
  if (!record2) {
128548
- return errorResponse2(404, "NOT_FOUND", "Authorization not found");
129791
+ return errorResponse3(404, "NOT_FOUND", "Authorization not found");
128549
129792
  }
128550
- return jsonResponse2(toMcpOAuthAuthorizationView(record2));
129793
+ return jsonResponse3(toMcpOAuthAuthorizationView(record2));
128551
129794
  }
128552
129795
  if (action === "approve") {
128553
129796
  if (input.request.method !== "POST") {
128554
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129797
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128555
129798
  }
128556
129799
  let body = {};
128557
129800
  try {
128558
129801
  body = await input.request.json();
128559
129802
  } catch {
128560
- return errorResponse2(400, "INVALID_BODY", "Body must be JSON");
129803
+ return errorResponse3(400, "INVALID_BODY", "Body must be JSON");
128561
129804
  }
128562
129805
  if (Array.isArray(body.scopes) && body.scopes.length > 0) {
128563
129806
  if (!input.gateway || !input.gatewayConfig?.url) {
128564
- return errorResponse2(500, "SERVER_NOT_CONFIGURED", "Gateway config is not configured");
129807
+ return errorResponse3(500, "SERVER_NOT_CONFIGURED", "Gateway config is not configured");
128565
129808
  }
128566
129809
  try {
128567
129810
  const approved = await approveMcpOAuthAuthorizationWithScopes({
@@ -128579,16 +129822,16 @@ async function handleMcpRoute(input) {
128579
129822
  serverSigner: input.serverSigner,
128580
129823
  now: input.now
128581
129824
  });
128582
- return jsonResponse2({ redirectTo: approved.redirectTo });
129825
+ return jsonResponse3({ redirectTo: approved.redirectTo });
128583
129826
  } catch (err2) {
128584
129827
  if (err2 instanceof McpOAuthAuthorizationError) {
128585
- return errorResponse2(err2.status, err2.code, err2.message);
129828
+ return errorResponse3(err2.status, err2.code, err2.message);
128586
129829
  }
128587
129830
  throw err2;
128588
129831
  }
128589
129832
  }
128590
129833
  if (!Array.isArray(body.grants) || body.grants.length === 0) {
128591
- return errorResponse2(400, "GRANTS_REQUIRED", "Approve requires grants or scopes");
129834
+ return errorResponse3(400, "GRANTS_REQUIRED", "Approve requires grants or scopes");
128592
129835
  }
128593
129836
  try {
128594
129837
  const approved = await approveMcpOAuthAuthorization({ authorizationId: id2, grants: body.grants }, {
@@ -128596,15 +129839,15 @@ async function handleMcpRoute(input) {
128596
129839
  authorizationStore: input.authorizationStore,
128597
129840
  now: input.now
128598
129841
  });
128599
- return jsonResponse2({ redirectTo: approved.redirectTo });
129842
+ return jsonResponse3({ redirectTo: approved.redirectTo });
128600
129843
  } catch (err2) {
128601
129844
  if (err2 instanceof McpOAuthAuthorizationError) {
128602
- return errorResponse2(err2.status, err2.code, err2.message);
129845
+ return errorResponse3(err2.status, err2.code, err2.message);
128603
129846
  }
128604
129847
  throw err2;
128605
129848
  }
128606
129849
  }
128607
- return errorResponse2(404, "NOT_FOUND", "Not found");
129850
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128608
129851
  }
128609
129852
  if (pathname === ownerPrefix || pathname.startsWith(`${ownerPrefix}/`)) {
128610
129853
  try {
@@ -128628,41 +129871,41 @@ async function handleMcpRoute(input) {
128628
129871
  publicOrigin: input.serverOrigin,
128629
129872
  now: input.now
128630
129873
  });
128631
- return jsonResponse2(created, { status: 201 });
129874
+ return jsonResponse3(created, { status: 201 });
128632
129875
  }
128633
129876
  if (input.request.method === "GET") {
128634
129877
  const records = await listMcpConnectionViews(input.store);
128635
- return jsonResponse2({ connections: records });
129878
+ return jsonResponse3({ connections: records });
128636
129879
  }
128637
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129880
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128638
129881
  }
128639
129882
  const tail = pathname.slice(ownerPrefix.length + 1);
128640
129883
  const [id2, action] = tail.split("/");
128641
129884
  if (!id2) {
128642
- return errorResponse2(404, "NOT_FOUND", "Not found");
129885
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128643
129886
  }
128644
129887
  if (action === "approve") {
128645
129888
  if (input.request.method !== "POST") {
128646
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129889
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128647
129890
  }
128648
129891
  let body = {};
128649
129892
  try {
128650
129893
  body = await input.request.json();
128651
129894
  } catch {
128652
- return errorResponse2(400, "INVALID_BODY", "Body must be JSON");
129895
+ return errorResponse3(400, "INVALID_BODY", "Body must be JSON");
128653
129896
  }
128654
129897
  if (!Array.isArray(body.grants) || body.grants.length === 0) {
128655
- return errorResponse2(400, "GRANTS_REQUIRED", "Approve requires at least one grant \u2014 mint grants in the consent flow first");
129898
+ return errorResponse3(400, "GRANTS_REQUIRED", "Approve requires at least one grant \u2014 mint grants in the consent flow first");
128656
129899
  }
128657
129900
  try {
128658
129901
  const updated = await approveMcpConnection({ connectionId: id2, grants: body.grants }, { store: input.store, now: input.now });
128659
- return jsonResponse2(toMcpConnectionView(updated));
129902
+ return jsonResponse3(toMcpConnectionView(updated));
128660
129903
  } catch (err2) {
128661
129904
  if (err2 instanceof McpConnectionNotFoundError) {
128662
- return errorResponse2(404, "NOT_FOUND", err2.message);
129905
+ return errorResponse3(404, "NOT_FOUND", err2.message);
128663
129906
  }
128664
129907
  if (err2 instanceof McpConnectionStateError) {
128665
- return errorResponse2(409, "INVALID_STATE", err2.message);
129908
+ return errorResponse3(409, "INVALID_STATE", err2.message);
128666
129909
  }
128667
129910
  throw err2;
128668
129911
  }
@@ -128674,22 +129917,22 @@ async function handleMcpRoute(input) {
128674
129917
  store: input.store,
128675
129918
  now: input.now
128676
129919
  });
128677
- return jsonResponse2(toMcpConnectionView(updated));
129920
+ return jsonResponse3(toMcpConnectionView(updated));
128678
129921
  } catch (err2) {
128679
129922
  if (err2 instanceof McpConnectionNotFoundError) {
128680
- return errorResponse2(404, "NOT_FOUND", err2.message);
129923
+ return errorResponse3(404, "NOT_FOUND", err2.message);
128681
129924
  }
128682
129925
  throw err2;
128683
129926
  }
128684
129927
  }
128685
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129928
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128686
129929
  }
128687
- return errorResponse2(404, "NOT_FOUND", "Not found");
129930
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128688
129931
  }
128689
129932
  async function handleMcpToken(rawToken, options) {
128690
129933
  if (!rawToken) {
128691
129934
  if (!options.oauthChallenge) {
128692
- return errorResponse2(401, "INVALID_TOKEN", "Missing MCP connection token");
129935
+ return errorResponse3(401, "INVALID_TOKEN", "Missing MCP connection token");
128693
129936
  }
128694
129937
  return mcpUnauthorized(input.serverOrigin);
128695
129938
  }
@@ -128697,7 +129940,7 @@ async function handleMcpRoute(input) {
128697
129940
  const record2 = await input.store.getByTokenHash(tokenHash);
128698
129941
  if (!record2) {
128699
129942
  if (!options.oauthChallenge) {
128700
- return errorResponse2(401, "INVALID_TOKEN", "Unknown or revoked MCP connection");
129943
+ return errorResponse3(401, "INVALID_TOKEN", "Unknown or revoked MCP connection");
128701
129944
  }
128702
129945
  return mcpUnauthorized(input.serverOrigin, "Unknown or revoked MCP connection");
128703
129946
  }
@@ -128741,10 +129984,10 @@ async function handleMcpRoute(input) {
128741
129984
  throw err2;
128742
129985
  }
128743
129986
  if (input.request.method !== "GET") {
128744
- return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
129987
+ return errorResponse3(405, "METHOD_NOT_ALLOWED", "Method not allowed");
128745
129988
  }
128746
129989
  const snapshot = input.activityRecorder ? input.activityRecorder.snapshot() : { events: [], running: 0, total: 0 };
128747
- return jsonResponse2(snapshot);
129990
+ return jsonResponse3(snapshot);
128748
129991
  }
128749
129992
  if (pathname === "/mcp") {
128750
129993
  return handleMcpToken(bearerToken(input.request), {
@@ -128755,7 +129998,7 @@ async function handleMcpRoute(input) {
128755
129998
  if (pathname.startsWith(mcpPrefix)) {
128756
129999
  const rawToken = decodeURIComponent(pathname.slice(mcpPrefix.length));
128757
130000
  if (!rawToken || rawToken.includes("/")) {
128758
- return errorResponse2(404, "NOT_FOUND", "Not found");
130001
+ return errorResponse3(404, "NOT_FOUND", "Not found");
128759
130002
  }
128760
130003
  return handleMcpToken(rawToken, { oauthChallenge: false });
128761
130004
  }
@@ -129402,6 +130645,24 @@ async function downloadOne(deps, record2) {
129402
130645
  }
129403
130646
  diagnostics?.onIndexEnd(record2.id, envelope.scope);
129404
130647
  logger.info({ dataPointId: record2.id, scope: envelope.scope, path: relativePath }, "Downloaded and indexed data point");
130648
+ if (deps.onDataPointIndexed) {
130649
+ try {
130650
+ let lineageSources;
130651
+ try {
130652
+ lineageSources = readStoredLineage(envelope.data)?.sources;
130653
+ } catch {
130654
+ }
130655
+ deps.onDataPointIndexed({
130656
+ scope: envelope.scope,
130657
+ dataPointId: record2.id,
130658
+ version: Number(record2.expectedVersion),
130659
+ collectedAt: envelope.collectedAt,
130660
+ lineageSources
130661
+ });
130662
+ } catch (err2) {
130663
+ logger.warn({ scope: envelope.scope, error: err2.message }, "onDataPointIndexed hook failed; data point already indexed");
130664
+ }
130665
+ }
129405
130666
  return {
129406
130667
  dataPointId: record2.id,
129407
130668
  scope: envelope.scope,
@@ -130481,7 +131742,8 @@ async function createPsLiteSyncManager(options) {
130481
131742
  logger,
130482
131743
  diagnostics: downloadDiagnostics,
130483
131744
  dataPointFeed,
130484
- scopeDeletions
131745
+ scopeDeletions,
131746
+ onDataPointIndexed: options.onDataPointIndexed
130485
131747
  }, {
130486
131748
  deleteData,
130487
131749
  pendingBlobDeletions,
@@ -130509,6 +131771,58 @@ async function createPsLiteSyncManager(options) {
130509
131771
  return { syncManager, serverOwner, dataPointFeed, scopeDeletions };
130510
131772
  }
130511
131773
 
131774
+ // ../lite/dist/derivatives.js
131775
+ var QUESTIONS_KEY = "derivative-questions-v1";
131776
+ async function createPsLiteQuestionStore(stateStore) {
131777
+ const saved = await stateStore.get(QUESTIONS_KEY);
131778
+ const initial = saved?.version === 1 ? saved.questions : [];
131779
+ return createInMemoryQuestionStore({
131780
+ initial,
131781
+ onChange: (questions) => stateStore.set(QUESTIONS_KEY, {
131782
+ version: 1,
131783
+ questions
131784
+ })
131785
+ });
131786
+ }
131787
+ function psLiteInferenceConfigured(config2) {
131788
+ return config2.inference.baseUrl.replace(/\/+$/, "") !== DEFAULT_INFERENCE_BASE_URL.replace(/\/+$/, "");
131789
+ }
131790
+ function createPsLiteDerivativeCompute(options) {
131791
+ const provider = options.provider ?? createOpenAiCompatibleInferenceProvider({
131792
+ baseUrl: options.config.inference.baseUrl,
131793
+ model: options.config.inference.model
131794
+ });
131795
+ const logger = options.logger ? {
131796
+ info: (payload, message) => options.logger?.info(payload, message),
131797
+ warn: (payload, message) => options.logger?.warn(payload, message)
131798
+ } : void 0;
131799
+ const scheduler = createRecomputeScheduler({
131800
+ store: options.store,
131801
+ debounceMs: options.config.inference.recomputeDebounceMs,
131802
+ serverOwner: options.serverOwner,
131803
+ now: options.now,
131804
+ logger,
131805
+ compute: (questionId) => computeQuestion(questionId, {
131806
+ // A -> B -> C: a question reading this derived scope recomputes.
131807
+ onDerivedWritten: (event) => scheduler.markSourceChanged(event.scope, {
131808
+ lineageSources: event.lineageSources
131809
+ }),
131810
+ runtimeAvailability: options.runtimeAvailability,
131811
+ storage: options.storage,
131812
+ store: options.store,
131813
+ provider,
131814
+ serverOwner: options.serverOwner,
131815
+ maxSourceItems: options.config.inference.maxSourceItems,
131816
+ syncManager: options.syncManager?.() ?? null,
131817
+ scopeDeletions: options.scopeDeletions?.(),
131818
+ writePolicyPorts: options.writePolicyPorts,
131819
+ now: options.now,
131820
+ logger
131821
+ })
131822
+ });
131823
+ return { store: options.store, scheduler, provider };
131824
+ }
131825
+
130512
131826
  // ../lite/dist/persistence.js
130513
131827
  function assertCompletePsLitePersistenceBundle(bundle) {
130514
131828
  if (!bundle || typeof bundle !== "object") {
@@ -130573,6 +131887,29 @@ async function createIndexedDbPsLiteRuntime(options) {
130573
131887
  });
130574
131888
  let syncManager = options.syncManager ?? null;
130575
131889
  let scopeDeletions = options.scopeDeletions;
131890
+ let runtimeRef = null;
131891
+ let derivatives = options.derivatives ?? null;
131892
+ if (derivatives === null && options.derivatives === void 0 && (options.inferenceProvider || psLiteInferenceConfigured(config2))) {
131893
+ derivatives = createPsLiteDerivativeCompute({
131894
+ config: config2,
131895
+ storage,
131896
+ store: await createPsLiteQuestionStore(stateStore),
131897
+ serverOwner,
131898
+ syncManager: () => syncManager,
131899
+ scopeDeletions: () => scopeDeletions,
131900
+ writePolicyPorts: {
131901
+ authSessionVerifier: gateway,
131902
+ grantVerifier: gateway
131903
+ },
131904
+ runtimeAvailability: {
131905
+ isAvailable: () => runtimeRef?.isAvailable() ?? Boolean(options.active)
131906
+ },
131907
+ provider: options.inferenceProvider,
131908
+ logger: options.logger
131909
+ });
131910
+ } else if (derivatives === null && options.derivatives === void 0) {
131911
+ options.logger?.warn({ baseUrl: config2.inference.baseUrl }, "Derivative compute disabled: inference.baseUrl is the direct-provider default; point it at the Vana inference relay");
131912
+ }
130576
131913
  if (!syncManager && config2.sync.enabled) {
130577
131914
  const sync = await createPsLiteSyncManager({
130578
131915
  config: config2,
@@ -130586,12 +131923,14 @@ async function createIndexedDbPsLiteRuntime(options) {
130586
131923
  scopeDeletions,
130587
131924
  diagnostics,
130588
131925
  logger: options.logger,
130589
- lineageGateway
131926
+ lineageGateway,
131927
+ onDataPointIndexed: (event) => derivatives?.scheduler.markSourceChanged(event.scope, {
131928
+ lineageSources: event.lineageSources
131929
+ })
130590
131930
  });
130591
131931
  syncManager = sync.syncManager;
130592
131932
  scopeDeletions = sync.scopeDeletions;
130593
131933
  }
130594
- let runtimeRef = null;
130595
131934
  const auth = options.auth ?? createWeb3SignedPsLiteAuth({
130596
131935
  origin: () => options.runtimeOrigin ?? config2.server.origin,
130597
131936
  ownerAddress: serverOwner,
@@ -130621,6 +131960,7 @@ async function createIndexedDbPsLiteRuntime(options) {
130621
131960
  scopeDeletions,
130622
131961
  diagnostics,
130623
131962
  lineageGateway,
131963
+ derivatives,
130624
131964
  saveConfig: async (nextConfig) => {
130625
131965
  const saved = await savePsLiteConfig(stateStore, nextConfig);
130626
131966
  Object.assign(config2, saved);
@@ -130643,7 +131983,8 @@ async function createIndexedDbPsLiteRuntime(options) {
130643
131983
  storage,
130644
131984
  tokenStore,
130645
131985
  accessLogStore,
130646
- syncManager
131986
+ syncManager,
131987
+ derivatives
130647
131988
  };
130648
131989
  }
130649
131990