@opendatalabs/personal-server-ts-server 1.13.1 → 1.14.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.
@@ -1 +1 @@
1
- {"version":3,"file":"question-store.d.ts","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,mDAAmD,CAAC;AAgE3D,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,aAAa,CAsFf"}
1
+ {"version":3,"file":"question-store.d.ts","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,mDAAmD,CAAC;AAgF3D,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,aAAa,CAmGf"}
@@ -15,12 +15,26 @@ CREATE TABLE IF NOT EXISTS derivative_questions (
15
15
  registered_by TEXT NOT NULL,
16
16
  status TEXT NOT NULL,
17
17
  error TEXT,
18
+ error_code TEXT,
18
19
  created_at TEXT NOT NULL,
19
20
  updated_at TEXT NOT NULL,
20
21
  last_computed_at TEXT,
21
22
  derived_version INTEGER,
22
23
  derived_collected_at TEXT
23
24
  )`;
25
+ /**
26
+ * Databases created before the column existed migrate in place. ALTER is
27
+ * guarded by a pragma check because SQLite has no ADD COLUMN IF NOT EXISTS;
28
+ * existing rows read back as null (pre-classification failures).
29
+ */
30
+ function ensureErrorCodeColumn(db) {
31
+ const columns = db
32
+ .prepare("PRAGMA table_info(derivative_questions)")
33
+ .all();
34
+ if (columns.some((column) => column.name === "error_code"))
35
+ return;
36
+ db.exec("ALTER TABLE derivative_questions ADD COLUMN error_code TEXT");
37
+ }
24
38
  const CREATE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS idx_derivative_questions_derived_scope ON derivative_questions (derived_scope)";
25
39
  // Databases created before the recompute policy existed migrate in place;
26
40
  // their registrations keep the old follow-every-change behavior.
@@ -36,6 +50,7 @@ function toRegistration(row) {
36
50
  registeredBy: JSON.parse(row.registered_by),
37
51
  status: row.status,
38
52
  error: row.error,
53
+ errorCode: row.error_code ?? null,
39
54
  createdAt: row.created_at,
40
55
  updatedAt: row.updated_at,
41
56
  lastComputedAt: row.last_computed_at,
@@ -45,6 +60,7 @@ function toRegistration(row) {
45
60
  }
46
61
  export function createSqliteQuestionStore(db) {
47
62
  db.exec(CREATE_TABLE_SQL);
63
+ ensureErrorCodeColumn(db);
48
64
  db.exec(CREATE_INDEX_SQL);
49
65
  const columns = db
50
66
  .prepare("PRAGMA table_info(derivative_questions)")
@@ -53,21 +69,25 @@ export function createSqliteQuestionStore(db) {
53
69
  db.exec(ADD_RECOMPUTE_COLUMN_SQL);
54
70
  }
55
71
  const listAll = db.prepare("SELECT * FROM derivative_questions ORDER BY created_at ASC, question_id ASC");
72
+ // The status route polls list({ derivedScope }) on every reader request;
73
+ // this is the query idx_derivative_questions_derived_scope exists for.
74
+ const listByDerivedScope = db.prepare("SELECT * FROM derivative_questions WHERE derived_scope = ? ORDER BY created_at ASC, question_id ASC");
56
75
  const getOne = db.prepare("SELECT * FROM derivative_questions WHERE question_id = ?");
57
76
  const insertOne = db.prepare(`
58
77
  INSERT INTO derivative_questions (
59
78
  question_id, derived_scope, source_scopes, question, model, recompute,
60
- registered_by, status, error, created_at, updated_at,
79
+ registered_by, status, error, error_code, created_at, updated_at,
61
80
  last_computed_at, derived_version, derived_collected_at
62
81
  ) VALUES (
63
82
  @question_id, @derived_scope, @source_scopes, @question, @model,
64
- @recompute, @registered_by, @status, @error, @created_at, @updated_at,
83
+ @recompute, @registered_by, @status, @error, @error_code, @created_at, @updated_at,
65
84
  @last_computed_at, @derived_version, @derived_collected_at
66
85
  )`);
67
86
  const updateOne = db.prepare(`
68
87
  UPDATE derivative_questions SET
69
88
  status = @status,
70
89
  error = @error,
90
+ error_code = @error_code,
71
91
  updated_at = @updated_at,
72
92
  last_computed_at = @last_computed_at,
73
93
  derived_version = @derived_version,
@@ -76,7 +96,10 @@ export function createSqliteQuestionStore(db) {
76
96
  const deleteOne = db.prepare("DELETE FROM derivative_questions WHERE question_id = ?");
77
97
  return {
78
98
  async list(filter) {
79
- return listAll.all()
99
+ const rows = filter?.derivedScope !== undefined
100
+ ? listByDerivedScope.all(filter.derivedScope)
101
+ : listAll.all();
102
+ return rows
80
103
  .map(toRegistration)
81
104
  .filter((registration) => matchesQuestionFilter(registration, filter));
82
105
  },
@@ -95,6 +118,7 @@ export function createSqliteQuestionStore(db) {
95
118
  registered_by: JSON.stringify(registration.registeredBy),
96
119
  status: registration.status,
97
120
  error: registration.error,
121
+ error_code: registration.errorCode,
98
122
  created_at: registration.createdAt,
99
123
  updated_at: registration.updatedAt,
100
124
  last_computed_at: registration.lastComputedAt,
@@ -111,6 +135,7 @@ export function createSqliteQuestionStore(db) {
111
135
  question_id: questionId,
112
136
  status: merged.status,
113
137
  error: merged.error,
138
+ error_code: merged.errorCode ?? null,
114
139
  updated_at: merged.updatedAt,
115
140
  last_computed_at: merged.lastComputedAt,
116
141
  derived_version: merged.derivedVersion,
@@ -1 +1 @@
1
- {"version":3,"file":"question-store.js","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"question-store.js","sourceRoot":"","sources":["../../src/storage/question-store.ts"],"names":[],"mappings":"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"}
@@ -114043,6 +114043,7 @@ function questionRegistrationView(registration) {
114043
114043
  registeredBy: registration.registeredBy,
114044
114044
  status: registration.status,
114045
114045
  error: registration.error,
114046
+ errorCode: registration.errorCode,
114046
114047
  createdAt: registration.createdAt,
114047
114048
  updatedAt: registration.updatedAt,
114048
114049
  lastComputedAt: registration.lastComputedAt,
@@ -114055,6 +114056,8 @@ function questionRegistrationView(registration) {
114055
114056
  function clone2(registration) {
114056
114057
  return {
114057
114058
  ...registration,
114059
+ // Registrations persisted before the field existed load as null.
114060
+ errorCode: registration.errorCode ?? null,
114058
114061
  sourceScopes: [...registration.sourceScopes],
114059
114062
  registeredBy: { ...registration.registeredBy }
114060
114063
  };
@@ -114241,6 +114244,7 @@ async function createQuestionRegistration(input) {
114241
114244
  registeredBy: input.registeredBy,
114242
114245
  status: "pending",
114243
114246
  error: null,
114247
+ errorCode: null,
114244
114248
  createdAt: at3,
114245
114249
  updatedAt: at3,
114246
114250
  lastComputedAt: null,
@@ -114619,11 +114623,25 @@ async function withRetries(deps, attempt, retryable) {
114619
114623
  }
114620
114624
  }
114621
114625
  var ComputeFailure = class extends Error {
114622
- constructor(message) {
114626
+ code;
114627
+ constructor(message, code) {
114623
114628
  super(message);
114624
114629
  this.name = "ComputeFailure";
114630
+ this.code = code;
114625
114631
  }
114626
114632
  };
114633
+ function classifyComputeError(err2) {
114634
+ if (err2 instanceof ComputeFailure)
114635
+ return err2.code ?? "internal";
114636
+ if (err2 instanceof InferenceRequestError) {
114637
+ return isRetryableInferenceError(err2) ? "inference_unavailable" : "internal";
114638
+ }
114639
+ if (err2 instanceof ProtocolError) {
114640
+ const grantShaped = err2.errorCode === "DERIVATIVE_SOURCE_NOT_GRANTED" || err2.errorCode === "SCOPE_MISMATCH" || err2.errorCode === "UNREGISTERED_BUILDER" || err2.errorCode.startsWith("GRANT_");
114641
+ return grantShaped ? "grant_invalid" : "internal";
114642
+ }
114643
+ return "internal";
114644
+ }
114627
114645
  function shortError(err2) {
114628
114646
  if (err2 instanceof ComputeFailure)
114629
114647
  return err2.message;
@@ -114708,10 +114726,10 @@ async function loadSource(deps, scope) {
114708
114726
  const entry = deps.storage.findEntry({ scope });
114709
114727
  const deletion = await resolveReadDeletion({ scopeDeletions: deps.scopeDeletions, serverOwner: deps.serverOwner }, scope, entry);
114710
114728
  if (deletion) {
114711
- throw new ComputeFailure(`source scope ${scope} is deleted`);
114729
+ throw new ComputeFailure(`source scope ${scope} is deleted`, "source_missing");
114712
114730
  }
114713
114731
  if (!entry) {
114714
- throw new ComputeFailure(`source scope ${scope} has no local data`);
114732
+ throw new ComputeFailure(`source scope ${scope} has no local data`, "source_missing");
114715
114733
  }
114716
114734
  let envelope;
114717
114735
  try {
@@ -114847,6 +114865,7 @@ async function computeQuestion(questionId, deps) {
114847
114865
  const updated = await deps.store.update(questionId, {
114848
114866
  status: "ready",
114849
114867
  error: null,
114868
+ errorCode: null,
114850
114869
  updatedAt: computedAt,
114851
114870
  lastComputedAt: computedAt,
114852
114871
  derivedVersion: entry?.version ?? null,
@@ -114884,28 +114903,49 @@ async function computeQuestion(questionId, deps) {
114884
114903
  };
114885
114904
  } catch (err2) {
114886
114905
  const error51 = shortError(err2);
114906
+ const errorCode = classifyComputeError(err2);
114887
114907
  const at3 = now().toISOString();
114888
114908
  const updated = await deps.store.update(questionId, {
114889
114909
  status: "failed",
114890
114910
  error: error51,
114911
+ errorCode,
114891
114912
  updatedAt: at3
114892
114913
  });
114893
114914
  deps.logger?.warn?.({ questionId, derivedScope: registration.derivedScope, error: error51 }, "Derivative question compute failed");
114894
114915
  return {
114895
114916
  status: "failed",
114896
- registration: updated ?? { ...registration, status: "failed", error: error51 },
114917
+ registration: updated ?? {
114918
+ ...registration,
114919
+ status: "failed",
114920
+ error: error51,
114921
+ errorCode
114922
+ },
114897
114923
  error: error51
114898
114924
  };
114899
114925
  }
114900
114926
  }
114901
114927
 
114902
114928
  // ../core/dist/derivatives/scheduler.js
114929
+ var DEFAULT_RETRY_DELAYS_MS2 = [6e4, 3e5, 18e5];
114930
+ function isRuntimeSkipOutcome(outcome) {
114931
+ if (typeof outcome !== "object" || outcome === null)
114932
+ return false;
114933
+ const shaped = outcome;
114934
+ return shaped.status === "skipped" && shaped.reason === "runtime-unavailable";
114935
+ }
114936
+ function isTransientFailureOutcome(outcome) {
114937
+ if (typeof outcome !== "object" || outcome === null)
114938
+ return false;
114939
+ const shaped = outcome;
114940
+ return shaped.status === "failed" && shaped.registration?.errorCode === "inference_unavailable";
114941
+ }
114903
114942
  var defaultTimers = {
114904
114943
  setTimeout: (callback, ms3) => setTimeout(callback, ms3),
114905
114944
  clearTimeout: (handle) => clearTimeout(handle)
114906
114945
  };
114907
114946
  function createRecomputeScheduler(options) {
114908
114947
  const debounceMs = options.debounceMs ?? 5e3;
114948
+ const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS2;
114909
114949
  const timers = options.timers ?? defaultTimers;
114910
114950
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
114911
114951
  const states = /* @__PURE__ */ new Map();
@@ -114924,7 +114964,15 @@ function createRecomputeScheduler(options) {
114924
114964
  function stateFor(questionId) {
114925
114965
  let state = states.get(questionId);
114926
114966
  if (!state) {
114927
- state = { timer: null, running: null, rerun: false };
114967
+ state = {
114968
+ timer: null,
114969
+ running: null,
114970
+ rerun: false,
114971
+ retryAttempts: 0,
114972
+ retryAtMs: null,
114973
+ retryScheduled: false,
114974
+ retryRunning: false
114975
+ };
114928
114976
  states.set(questionId, state);
114929
114977
  }
114930
114978
  return state;
@@ -114937,17 +114985,42 @@ function createRecomputeScheduler(options) {
114937
114985
  state.rerun = true;
114938
114986
  return;
114939
114987
  }
114940
- state.running = track(Promise.resolve().then(() => options.compute(questionId)).then(() => void 0, (err2) => warn({
114941
- questionId,
114942
- error: err2 instanceof Error ? err2.name : String(err2)
114943
- }, "Derivative compute threw")).then(async () => {
114988
+ state.running = track(Promise.resolve().then(() => options.compute(questionId)).then((outcome) => outcome, (err2) => {
114989
+ warn({
114990
+ questionId,
114991
+ error: err2 instanceof Error ? err2.name : String(err2)
114992
+ }, "Derivative compute threw");
114993
+ return void 0;
114994
+ }).then(async (outcome) => {
114944
114995
  state.running = null;
114996
+ state.retryAtMs = null;
114997
+ state.retryRunning = false;
114998
+ const retryable = isTransientFailureOutcome(outcome) || // A runtime-unavailable skip DURING a chain consumes an attempt
114999
+ // instead of ending the chain; on a fresh compute it keeps the
115000
+ // pre-retry behavior (pending/stale, reswept by start()).
115001
+ isRuntimeSkipOutcome(outcome) && state.retryAttempts > 0;
114945
115002
  if (state.rerun) {
114946
115003
  state.rerun = false;
115004
+ state.retryAttempts = 0;
114947
115005
  await markStale(questionId);
114948
115006
  schedule(questionId, 0);
114949
- } else if (!states.get(questionId)?.timer) {
114950
- states.delete(questionId);
115007
+ } else if (retryable && // A pending timer means a source change or explicit recompute
115008
+ // arrived during the run and already scheduled a fresher run;
115009
+ // replacing its 5s debounce with a 60s retry would delay the
115010
+ // recompute of data this compute never saw.
115011
+ state.timer === null && // After stop() nothing may be scheduled; without this a compute
115012
+ // that settles late would record a retryAtMs no timer backs.
115013
+ !stopped && state.retryAttempts < retryDelaysMs.length) {
115014
+ const delayMs = retryDelaysMs[state.retryAttempts];
115015
+ state.retryAttempts += 1;
115016
+ state.retryAtMs = now().getTime() + delayMs;
115017
+ schedule(questionId, delayMs);
115018
+ state.retryScheduled = true;
115019
+ } else {
115020
+ state.retryAttempts = 0;
115021
+ if (!states.get(questionId)?.timer) {
115022
+ states.delete(questionId);
115023
+ }
114951
115024
  }
114952
115025
  }));
114953
115026
  }
@@ -114959,6 +115032,9 @@ function createRecomputeScheduler(options) {
114959
115032
  timers.clearTimeout(state.timer);
114960
115033
  state.timer = timers.setTimeout(() => {
114961
115034
  state.timer = null;
115035
+ state.retryAtMs = null;
115036
+ state.retryRunning = state.retryScheduled;
115037
+ state.retryScheduled = false;
114962
115038
  run(questionId);
114963
115039
  }, delayMs);
114964
115040
  }
@@ -114969,11 +115045,29 @@ function createRecomputeScheduler(options) {
114969
115045
  if (registration.status === "ready" || registration.status === "failed") {
114970
115046
  await options.store.update(questionId, {
114971
115047
  status: "stale",
115048
+ // The reader contract says errorCode is null unless failed; a stale
115049
+ // question is a recompute in progress, not a terminal failure.
115050
+ errorCode: null,
114972
115051
  updatedAt: now().toISOString()
114973
115052
  });
114974
115053
  }
114975
115054
  }
115055
+ function resetRetry(questionId) {
115056
+ const state = states.get(questionId);
115057
+ if (!state)
115058
+ return;
115059
+ state.retryAttempts = 0;
115060
+ state.retryAtMs = null;
115061
+ state.retryScheduled = false;
115062
+ }
114976
115063
  return {
115064
+ nextRetryAt(questionId) {
115065
+ const retryAtMs = states.get(questionId)?.retryAtMs;
115066
+ return retryAtMs == null ? null : new Date(retryAtMs).toISOString();
115067
+ },
115068
+ retryInFlight(questionId) {
115069
+ return states.get(questionId)?.retryRunning ?? false;
115070
+ },
114977
115071
  markSourceChanged(scope, opts) {
114978
115072
  if (stopped)
114979
115073
  return;
@@ -114988,6 +115082,7 @@ function createRecomputeScheduler(options) {
114988
115082
  continue;
114989
115083
  }
114990
115084
  await markStale(registration.questionId);
115085
+ resetRetry(registration.questionId);
114991
115086
  schedule(registration.questionId, debounceMs);
114992
115087
  }
114993
115088
  })().catch((err2) => warn({ scope, error: err2 instanceof Error ? err2.name : String(err2) }, "Could not mark derivative questions stale")));
@@ -114998,7 +115093,10 @@ function createRecomputeScheduler(options) {
114998
115093
  void track(markStale(questionId).catch((err2) => warn({
114999
115094
  questionId,
115000
115095
  error: err2 instanceof Error ? err2.name : String(err2)
115001
- }, "Could not mark derivative question stale")).then(() => schedule(questionId, opts?.immediate ? 0 : debounceMs)));
115096
+ }, "Could not mark derivative question stale")).then(() => {
115097
+ resetRetry(questionId);
115098
+ schedule(questionId, opts?.immediate ? 0 : debounceMs);
115099
+ }));
115002
115100
  },
115003
115101
  async whenIdle() {
115004
115102
  const waitForTimers = !options.timers;
@@ -115018,6 +115116,9 @@ function createRecomputeScheduler(options) {
115018
115116
  if (state.timer !== null)
115019
115117
  timers.clearTimeout(state.timer);
115020
115118
  state.timer = null;
115119
+ state.retryAttempts = 0;
115120
+ state.retryAtMs = null;
115121
+ state.retryScheduled = false;
115021
115122
  }
115022
115123
  },
115023
115124
  start() {
@@ -115038,6 +115139,7 @@ function createRecomputeScheduler(options) {
115038
115139
 
115039
115140
  // ../core/dist/derivatives/api.js
115040
115141
  var MAX_QUESTION_BODY_BYTES = 16 * 1024;
115142
+ var RETRY_IN_FLIGHT_POLL_SECONDS = 5;
115041
115143
  function jsonResponse3(body, init) {
115042
115144
  const headers = new Headers(init?.headers);
115043
115145
  headers.set("Content-Type", "application/json");
@@ -115090,12 +115192,63 @@ async function loadForCaller(deps, request2, store, questionId) {
115090
115192
  }
115091
115193
  return { registration, writer };
115092
115194
  }
115195
+ async function handleStatusRoute(request2, url2, deps, store, scheduler, now) {
115196
+ if (request2.method !== "GET") {
115197
+ return errorResponse2(405, "METHOD_NOT_ALLOWED", "Method not allowed");
115198
+ }
115199
+ const derivedScopeParam = url2.searchParams.get("derivedScope");
115200
+ if (!derivedScopeParam) {
115201
+ throw new DerivativeDerivedScopeRequiredError();
115202
+ }
115203
+ const scopeResult = parseDataScopeContract(derivedScopeParam);
115204
+ if (!scopeResult.ok) {
115205
+ return errorResponse2(scopeResult.status, scopeResult.body.error, scopeResult.body.message);
115206
+ }
115207
+ const derivedScope = scopeResult.scope;
115208
+ const auth = await deps.auth.authorizeBuilderRead({
115209
+ request: request2,
115210
+ scope: derivedScope,
115211
+ grantId: selectedGrantId(request2, url2)
115212
+ }) ?? void 0;
115213
+ const registrations = await store.list({ derivedScope });
115214
+ if (registrations.length === 0) {
115215
+ throw new DerivativeQuestionNotFoundError({ derivedScope });
115216
+ }
115217
+ const STATUS_PRECEDENCE = {
115218
+ ready: 0,
115219
+ stale: 1,
115220
+ pending: 2,
115221
+ failed: 3
115222
+ };
115223
+ const registration = registrations.reduce((best, candidate) => {
115224
+ const byStatus = STATUS_PRECEDENCE[candidate.status] - STATUS_PRECEDENCE[best.status];
115225
+ if (byStatus !== 0)
115226
+ return byStatus < 0 ? candidate : best;
115227
+ return candidate.updatedAt >= best.updatedAt ? candidate : best;
115228
+ });
115229
+ const nextRetryAt = scheduler.nextRetryAt?.(registration.questionId) ?? null;
115230
+ const retryAfterSeconds = nextRetryAt !== null ? Math.max(1, Math.ceil((Date.parse(nextRetryAt) - now().getTime()) / 1e3)) : scheduler.retryInFlight?.(registration.questionId) ? RETRY_IN_FLIGHT_POLL_SECONDS : null;
115231
+ const registrarView = auth === void 0 || auth.grantId === "owner" || auth.grantId === "policy-bypass" || registration.registeredBy.kind === "builder" && typeof auth.builder === "string" && sameBuilder(registration.registeredBy.builder, auth.builder);
115232
+ const disclosedErrorCode = registration.errorCode === "grant_invalid" && !registrarView ? "internal" : registration.errorCode;
115233
+ return jsonResponse3({
115234
+ derivedScope: registration.derivedScope,
115235
+ status: registration.status,
115236
+ lastComputedAt: registration.lastComputedAt,
115237
+ derivedVersion: registration.derivedVersion,
115238
+ derivedCollectedAt: registration.derivedCollectedAt,
115239
+ // Null unless failed, whatever an old store row holds: a stale question
115240
+ // is a recompute in progress, not a terminal failure.
115241
+ errorCode: registration.status === "failed" ? disclosedErrorCode : null,
115242
+ retryAfterSeconds
115243
+ });
115244
+ }
115093
115245
  async function handlePersonalServerDerivativesRequest(request2, deps, options = {}) {
115094
115246
  try {
115095
115247
  const url2 = new URL(request2.url);
115096
115248
  const pathname = stripBasePath2(url2.pathname, options.basePath);
115097
115249
  const parts = pathname.split("/").filter(Boolean);
115098
- if (parts[0] !== "questions" || parts.length > 3) {
115250
+ const isStatusRoute = parts[0] === "status" && parts.length === 1;
115251
+ if (!isStatusRoute && (parts[0] !== "questions" || parts.length > 3)) {
115099
115252
  return errorResponse2(404, "NOT_FOUND", "Not found");
115100
115253
  }
115101
115254
  const compute = deps.compute;
@@ -115103,6 +115256,9 @@ async function handlePersonalServerDerivativesRequest(request2, deps, options =
115103
115256
  throw new DerivativeComputeUnavailableError();
115104
115257
  const { store, scheduler } = compute;
115105
115258
  const now = deps.now ?? (() => /* @__PURE__ */ new Date());
115259
+ if (isStatusRoute) {
115260
+ return await handleStatusRoute(request2, url2, deps, store, scheduler, now);
115261
+ }
115106
115262
  if (parts.length === 1) {
115107
115263
  if (request2.method === "GET") {
115108
115264
  const derivedScope = url2.searchParams.get("derivedScope") ?? void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/personal-server-ts-server",
3
- "version": "1.13.1",
3
+ "version": "1.14.0",
4
4
  "description": "Hono HTTP server for the Vana Personal Server — routes, middleware, composition root",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@hono/node-server": "^1.19.13",
47
- "@opendatalabs/personal-server-ts-core": "1.13.1",
48
- "@opendatalabs/personal-server-ts-lite": "1.13.1",
47
+ "@opendatalabs/personal-server-ts-core": "1.14.0",
48
+ "@opendatalabs/personal-server-ts-lite": "1.14.0",
49
49
  "@opendatalabs/vana-sdk": "3.14.0",
50
50
  "better-sqlite3": "^12.11.1",
51
51
  "hono": "^4.12.27",