@opendatalabs/personal-server-ts-server 1.14.0 → 1.15.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,EAKL,KAAK,aAAa,EACnB,MAAM,mDAAmD,CAAC;AAgF3D,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,aAAa,CAmGf"}
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,EAML,KAAK,aAAa,EACnB,MAAM,mDAAmD,CAAC;AAkG3D,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GACpB,aAAa,CAwGf"}
@@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS derivative_questions (
11
11
  source_scopes TEXT NOT NULL,
12
12
  question TEXT NOT NULL,
13
13
  model TEXT,
14
+ answer_shape TEXT,
14
15
  recompute TEXT NOT NULL DEFAULT 'on-change',
15
16
  registered_by TEXT NOT NULL,
16
17
  status TEXT NOT NULL,
@@ -35,6 +36,19 @@ function ensureErrorCodeColumn(db) {
35
36
  return;
36
37
  db.exec("ALTER TABLE derivative_questions ADD COLUMN error_code TEXT");
37
38
  }
39
+ /**
40
+ * Same in-place migration for the declared answer shape (stored as its
41
+ * JSON): rows written before it existed read back as null, which is the
42
+ * free-text answer the question was registered with.
43
+ */
44
+ function ensureAnswerShapeColumn(db) {
45
+ const columns = db
46
+ .prepare("PRAGMA table_info(derivative_questions)")
47
+ .all();
48
+ if (columns.some((column) => column.name === "answer_shape"))
49
+ return;
50
+ db.exec("ALTER TABLE derivative_questions ADD COLUMN answer_shape TEXT");
51
+ }
38
52
  const CREATE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS idx_derivative_questions_derived_scope ON derivative_questions (derived_scope)";
39
53
  // Databases created before the recompute policy existed migrate in place;
40
54
  // their registrations keep the old follow-every-change behavior.
@@ -46,6 +60,9 @@ function toRegistration(row) {
46
60
  sourceScopes: JSON.parse(row.source_scopes),
47
61
  question: row.question,
48
62
  model: row.model,
63
+ answerShape: row.answer_shape
64
+ ? JSON.parse(row.answer_shape)
65
+ : null,
49
66
  recompute: row.recompute,
50
67
  registeredBy: JSON.parse(row.registered_by),
51
68
  status: row.status,
@@ -61,6 +78,7 @@ function toRegistration(row) {
61
78
  export function createSqliteQuestionStore(db) {
62
79
  db.exec(CREATE_TABLE_SQL);
63
80
  ensureErrorCodeColumn(db);
81
+ ensureAnswerShapeColumn(db);
64
82
  db.exec(CREATE_INDEX_SQL);
65
83
  const columns = db
66
84
  .prepare("PRAGMA table_info(derivative_questions)")
@@ -75,12 +93,13 @@ export function createSqliteQuestionStore(db) {
75
93
  const getOne = db.prepare("SELECT * FROM derivative_questions WHERE question_id = ?");
76
94
  const insertOne = db.prepare(`
77
95
  INSERT INTO derivative_questions (
78
- question_id, derived_scope, source_scopes, question, model, recompute,
79
- registered_by, status, error, error_code, created_at, updated_at,
96
+ question_id, derived_scope, source_scopes, question, model, answer_shape,
97
+ recompute, registered_by, status, error, error_code, created_at, updated_at,
80
98
  last_computed_at, derived_version, derived_collected_at
81
99
  ) VALUES (
82
100
  @question_id, @derived_scope, @source_scopes, @question, @model,
83
- @recompute, @registered_by, @status, @error, @error_code, @created_at, @updated_at,
101
+ @answer_shape, @recompute, @registered_by, @status, @error, @error_code,
102
+ @created_at, @updated_at,
84
103
  @last_computed_at, @derived_version, @derived_collected_at
85
104
  )`);
86
105
  const updateOne = db.prepare(`
@@ -114,6 +133,9 @@ export function createSqliteQuestionStore(db) {
114
133
  source_scopes: JSON.stringify(registration.sourceScopes),
115
134
  question: registration.question,
116
135
  model: registration.model,
136
+ answer_shape: registration.answerShape
137
+ ? JSON.stringify(registration.answerShape)
138
+ : null,
117
139
  recompute: registration.recompute,
118
140
  registered_by: JSON.stringify(registration.registeredBy),
119
141
  status: registration.status,
@@ -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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"}
@@ -61644,8 +61644,8 @@ var require_multipleOf = __commonJS({
61644
61644
  const { gen: gen2, data, schemaCode, it: it3 } = cxt;
61645
61645
  const prec = it3.opts.multipleOfPrecision;
61646
61646
  const res = gen2.let("res");
61647
- const invalid = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
61648
- cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`);
61647
+ const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
61648
+ cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
61649
61649
  }
61650
61650
  };
61651
61651
  exports.default = def;
@@ -67542,8 +67542,8 @@ var require_multipleOf2 = __commonJS({
67542
67542
  const { gen: gen2, data, schemaCode, it: it3 } = cxt;
67543
67543
  const prec = it3.opts.multipleOfPrecision;
67544
67544
  const res = gen2.let("res");
67545
- const invalid = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
67546
- cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`);
67545
+ const invalid2 = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`;
67546
+ cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid2}))`);
67547
67547
  }
67548
67548
  };
67549
67549
  exports.default = def;
@@ -114031,6 +114031,258 @@ async function loadOrCreatePsLiteServerIdentity(params) {
114031
114031
  return { persisted, account };
114032
114032
  }
114033
114033
 
114034
+ // ../core/dist/derivatives/answer-shape.js
114035
+ var MAX_ANSWER_SHAPE_FIELDS = 16;
114036
+ var MAX_ANSWER_FIELD_NAME_CHARS = 64;
114037
+ var MAX_ANSWER_STRING_CHARS = 4e3;
114038
+ var MAX_ANSWER_ENUM_VALUES = 32;
114039
+ var MAX_ANSWER_ENUM_VALUE_CHARS = 64;
114040
+ var FIELD_NAME = /^[A-Za-z][A-Za-z0-9_]*$/;
114041
+ var FIELD_TYPES = [
114042
+ "string",
114043
+ "number",
114044
+ "integer",
114045
+ "boolean",
114046
+ "enum"
114047
+ ];
114048
+ var KEYS_BY_TYPE = {
114049
+ string: ["name", "type", "required", "maxLength"],
114050
+ number: ["name", "type", "required", "min", "max"],
114051
+ integer: ["name", "type", "required", "min", "max"],
114052
+ boolean: ["name", "type", "required"],
114053
+ enum: ["name", "type", "required", "values"]
114054
+ };
114055
+ function isRecord4(value) {
114056
+ return value !== null && typeof value === "object" && !Array.isArray(value);
114057
+ }
114058
+ function invalid(message, details) {
114059
+ throw new DerivativeQuestionInvalidError(message, {
114060
+ field: "answerShape",
114061
+ ...details
114062
+ });
114063
+ }
114064
+ function parseFieldName(value, index) {
114065
+ if (typeof value !== "string" || value === "") {
114066
+ invalid(`answerShape.fields[${index}].name must be a non-empty string`, {
114067
+ index
114068
+ });
114069
+ }
114070
+ if (value.length > MAX_ANSWER_FIELD_NAME_CHARS) {
114071
+ invalid(`answerShape.fields[${index}].name is ${value.length} characters; the maximum is ${MAX_ANSWER_FIELD_NAME_CHARS}`, { index, max: MAX_ANSWER_FIELD_NAME_CHARS });
114072
+ }
114073
+ if (!FIELD_NAME.test(value)) {
114074
+ invalid(`answerShape.fields[${index}].name must start with a letter and hold only letters, digits and underscores`, { index });
114075
+ }
114076
+ return value;
114077
+ }
114078
+ function parseBound(value, index, key, integer2) {
114079
+ if (typeof value !== "number" || !Number.isFinite(value)) {
114080
+ invalid(`answerShape.fields[${index}].${key} must be a finite number`, {
114081
+ index,
114082
+ key
114083
+ });
114084
+ }
114085
+ if (integer2 && !Number.isSafeInteger(value)) {
114086
+ invalid(`answerShape.fields[${index}].${key} must be a safe integer for an integer field`, { index, key });
114087
+ }
114088
+ return value;
114089
+ }
114090
+ function parseEnumValues(value, index) {
114091
+ if (!Array.isArray(value) || value.length === 0) {
114092
+ invalid(`answerShape.fields[${index}].values must be a non-empty array of strings`, { index });
114093
+ }
114094
+ if (value.length > MAX_ANSWER_ENUM_VALUES) {
114095
+ invalid(`answerShape.fields[${index}].values lists ${value.length} values; the maximum is ${MAX_ANSWER_ENUM_VALUES}`, { index, max: MAX_ANSWER_ENUM_VALUES });
114096
+ }
114097
+ const values = [];
114098
+ for (const entry of value) {
114099
+ if (typeof entry !== "string" || entry === "") {
114100
+ invalid(`answerShape.fields[${index}].values must hold non-empty strings`, { index });
114101
+ }
114102
+ if (entry.length > MAX_ANSWER_ENUM_VALUE_CHARS) {
114103
+ invalid(`answerShape.fields[${index}].values holds a ${entry.length} character value; the maximum is ${MAX_ANSWER_ENUM_VALUE_CHARS}`, { index, max: MAX_ANSWER_ENUM_VALUE_CHARS });
114104
+ }
114105
+ if (/[\n\r\t]/.test(entry)) {
114106
+ invalid(`answerShape.fields[${index}].values must not hold line breaks or tabs`, { index });
114107
+ }
114108
+ if (values.includes(entry)) {
114109
+ invalid(`answerShape.fields[${index}].values lists the same value twice`, { index });
114110
+ }
114111
+ values.push(entry);
114112
+ }
114113
+ return values;
114114
+ }
114115
+ function parseField(value, index) {
114116
+ if (!isRecord4(value)) {
114117
+ invalid(`answerShape.fields[${index}] must be an object`, { index });
114118
+ }
114119
+ const name = parseFieldName(value.name, index);
114120
+ if (typeof value.type !== "string" || !FIELD_TYPES.includes(value.type)) {
114121
+ invalid(`answerShape.fields[${index}].type must be one of ${FIELD_TYPES.join(", ")}`, { index });
114122
+ }
114123
+ const type = value.type;
114124
+ const allowed = KEYS_BY_TYPE[type];
114125
+ for (const key of Object.keys(value)) {
114126
+ if (!allowed.includes(key)) {
114127
+ invalid(`answerShape.fields[${index}] does not accept "${key}" on a ${type} field`, { index, key: key.slice(0, MAX_ANSWER_FIELD_NAME_CHARS) });
114128
+ }
114129
+ }
114130
+ let required2 = true;
114131
+ if (value.required !== void 0 && value.required !== null) {
114132
+ if (typeof value.required !== "boolean") {
114133
+ invalid(`answerShape.fields[${index}].required must be a boolean`, {
114134
+ index
114135
+ });
114136
+ }
114137
+ required2 = value.required;
114138
+ }
114139
+ const field = { name, type, required: required2 };
114140
+ if (type === "string") {
114141
+ let maxLength = MAX_ANSWER_STRING_CHARS;
114142
+ if (value.maxLength !== void 0 && value.maxLength !== null) {
114143
+ if (typeof value.maxLength !== "number" || !Number.isSafeInteger(value.maxLength) || value.maxLength < 1 || value.maxLength > MAX_ANSWER_STRING_CHARS) {
114144
+ invalid(`answerShape.fields[${index}].maxLength must be an integer between 1 and ${MAX_ANSWER_STRING_CHARS}`, { index, max: MAX_ANSWER_STRING_CHARS });
114145
+ }
114146
+ maxLength = value.maxLength;
114147
+ }
114148
+ field.maxLength = maxLength;
114149
+ }
114150
+ if (type === "number" || type === "integer") {
114151
+ const integer2 = type === "integer";
114152
+ if (value.min !== void 0 && value.min !== null) {
114153
+ field.min = parseBound(value.min, index, "min", integer2);
114154
+ }
114155
+ if (value.max !== void 0 && value.max !== null) {
114156
+ field.max = parseBound(value.max, index, "max", integer2);
114157
+ }
114158
+ if (field.min !== void 0 && field.max !== void 0 && field.min > field.max) {
114159
+ invalid(`answerShape.fields[${index}].min is greater than its max`, {
114160
+ index
114161
+ });
114162
+ }
114163
+ }
114164
+ if (type === "enum") {
114165
+ field.values = parseEnumValues(value.values, index);
114166
+ }
114167
+ return field;
114168
+ }
114169
+ function parseAnswerShapeInput(value) {
114170
+ if (value === void 0 || value === null)
114171
+ return null;
114172
+ if (!isRecord4(value)) {
114173
+ invalid("answerShape must be an object with a fields array", {});
114174
+ }
114175
+ for (const key of Object.keys(value)) {
114176
+ if (key !== "fields") {
114177
+ invalid(`answerShape does not accept "${key}"`, {
114178
+ key: key.slice(0, MAX_ANSWER_FIELD_NAME_CHARS)
114179
+ });
114180
+ }
114181
+ }
114182
+ if (!Array.isArray(value.fields) || value.fields.length === 0) {
114183
+ invalid("answerShape.fields must be a non-empty array", {});
114184
+ }
114185
+ if (value.fields.length > MAX_ANSWER_SHAPE_FIELDS) {
114186
+ invalid(`answerShape.fields lists ${value.fields.length} fields; the maximum is ${MAX_ANSWER_SHAPE_FIELDS}`, { max: MAX_ANSWER_SHAPE_FIELDS });
114187
+ }
114188
+ const fields = [];
114189
+ const names = /* @__PURE__ */ new Set();
114190
+ value.fields.forEach((entry, index) => {
114191
+ const field = parseField(entry, index);
114192
+ if (names.has(field.name)) {
114193
+ invalid(`answerShape.fields names "${field.name}" twice`, { index });
114194
+ }
114195
+ names.add(field.name);
114196
+ fields.push(field);
114197
+ });
114198
+ return { fields };
114199
+ }
114200
+ function compileAnswerShape(shape) {
114201
+ const entries = {};
114202
+ for (const field of shape.fields) {
114203
+ let schema;
114204
+ switch (field.type) {
114205
+ case "string":
114206
+ schema = external_exports.string().max(field.maxLength ?? MAX_ANSWER_STRING_CHARS);
114207
+ break;
114208
+ case "number":
114209
+ case "integer": {
114210
+ let numeric = field.type === "integer" ? external_exports.int() : external_exports.number();
114211
+ if (field.min !== void 0)
114212
+ numeric = numeric.min(field.min);
114213
+ if (field.max !== void 0)
114214
+ numeric = numeric.max(field.max);
114215
+ schema = numeric;
114216
+ break;
114217
+ }
114218
+ case "boolean":
114219
+ schema = external_exports.boolean();
114220
+ break;
114221
+ case "enum":
114222
+ schema = external_exports.enum(field.values ?? []);
114223
+ break;
114224
+ }
114225
+ entries[field.name] = field.required ? schema : schema.nullish();
114226
+ }
114227
+ return external_exports.object(entries);
114228
+ }
114229
+ function validateAnswerShape(shape, compiled, candidate) {
114230
+ const parsed = compiled.safeParse(candidate);
114231
+ if (!parsed.success) {
114232
+ return {
114233
+ ok: false,
114234
+ // zod's messages state the constraint ("expected int", "<=5") and
114235
+ // never echo the received value, so they are safe to hand back to
114236
+ // the model in the corrective turn.
114237
+ issues: parsed.error.issues.map((issue2) => {
114238
+ const path = issue2.path.join(".");
114239
+ return path ? `${path}: ${issue2.message}` : issue2.message;
114240
+ })
114241
+ };
114242
+ }
114243
+ const value = {};
114244
+ for (const field of shape.fields) {
114245
+ const entry = parsed.data[field.name];
114246
+ if (entry === void 0 || entry === null)
114247
+ continue;
114248
+ value[field.name] = entry;
114249
+ }
114250
+ return { ok: true, value };
114251
+ }
114252
+ function describeField(field) {
114253
+ const requirement = field.required ? "required" : "optional";
114254
+ switch (field.type) {
114255
+ case "string":
114256
+ return `"${field.name}": string, at most ${field.maxLength ?? MAX_ANSWER_STRING_CHARS} characters (${requirement})`;
114257
+ case "number":
114258
+ case "integer": {
114259
+ const kind = field.type === "integer" ? "integer" : "number";
114260
+ const range = field.min !== void 0 && field.max !== void 0 ? ` between ${field.min} and ${field.max}` : field.min !== void 0 ? ` of at least ${field.min}` : field.max !== void 0 ? ` of at most ${field.max}` : "";
114261
+ return `"${field.name}": ${kind}${range} (${requirement})`;
114262
+ }
114263
+ case "boolean":
114264
+ return `"${field.name}": true or false (${requirement})`;
114265
+ case "enum":
114266
+ return `"${field.name}": exactly one of ${(field.values ?? []).map((entry) => JSON.stringify(entry)).join(", ")} (${requirement})`;
114267
+ }
114268
+ }
114269
+ function describeAnswerShape(shape) {
114270
+ return shape.fields.map((field) => ` ${describeField(field)}`);
114271
+ }
114272
+ function renderShapedAnswer(shape, value) {
114273
+ return shape.fields.filter((field) => value[field.name] !== void 0).map((field) => `${field.name}: ${String(value[field.name])}`).join("\n");
114274
+ }
114275
+ function cloneAnswerShape(shape) {
114276
+ if (!shape)
114277
+ return null;
114278
+ return {
114279
+ fields: shape.fields.map((field) => ({
114280
+ ...field,
114281
+ ...field.values ? { values: [...field.values] } : {}
114282
+ }))
114283
+ };
114284
+ }
114285
+
114034
114286
  // ../core/dist/derivatives/types.js
114035
114287
  function questionRegistrationView(registration) {
114036
114288
  return {
@@ -114039,6 +114291,7 @@ function questionRegistrationView(registration) {
114039
114291
  sourceScopes: [...registration.sourceScopes],
114040
114292
  question: registration.question,
114041
114293
  model: registration.model,
114294
+ answerShape: cloneAnswerShape(registration.answerShape),
114042
114295
  recompute: registration.recompute,
114043
114296
  registeredBy: registration.registeredBy,
114044
114297
  status: registration.status,
@@ -114058,6 +114311,7 @@ function clone2(registration) {
114058
114311
  ...registration,
114059
114312
  // Registrations persisted before the field existed load as null.
114060
114313
  errorCode: registration.errorCode ?? null,
114314
+ answerShape: cloneAnswerShape(registration.answerShape ?? null),
114061
114315
  sourceScopes: [...registration.sourceScopes],
114062
114316
  registeredBy: { ...registration.registeredBy }
114063
114317
  };
@@ -114128,7 +114382,7 @@ var MAX_QUESTION_CHARS = 8e3;
114128
114382
  var MAX_MODEL_CHARS = 128;
114129
114383
  var MAX_ECHOED_SCOPE_CHARS = 128;
114130
114384
  var MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
114131
- function isRecord4(value) {
114385
+ function isRecord5(value) {
114132
114386
  return value !== null && typeof value === "object" && !Array.isArray(value);
114133
114387
  }
114134
114388
  function parseScope(value, field) {
@@ -114144,7 +114398,7 @@ function parseScope(value, field) {
114144
114398
  return parsed.scope;
114145
114399
  }
114146
114400
  function parseQuestionInput(body) {
114147
- if (!isRecord4(body)) {
114401
+ if (!isRecord5(body)) {
114148
114402
  throw new DerivativeQuestionInvalidError("Body must be a JSON object");
114149
114403
  }
114150
114404
  const derivedScope = parseScope(body.derivedScope, "derivedScope");
@@ -114178,6 +114432,7 @@ function parseQuestionInput(body) {
114178
114432
  }
114179
114433
  model = body.model;
114180
114434
  }
114435
+ const answerShape = parseAnswerShapeInput(body.answerShape);
114181
114436
  let recompute = "on-change";
114182
114437
  if (body.recompute !== void 0 && body.recompute !== null) {
114183
114438
  if (body.recompute !== "snapshot" && body.recompute !== "on-change") {
@@ -114191,6 +114446,7 @@ function parseQuestionInput(body) {
114191
114446
  sourceScopes,
114192
114447
  question: body.question,
114193
114448
  model,
114449
+ answerShape,
114194
114450
  recompute
114195
114451
  };
114196
114452
  }
@@ -114240,6 +114496,7 @@ async function createQuestionRegistration(input) {
114240
114496
  sourceScopes: parsed.sourceScopes,
114241
114497
  question: parsed.question,
114242
114498
  model: parsed.model,
114499
+ answerShape: parsed.answerShape,
114243
114500
  recompute: parsed.recompute,
114244
114501
  registeredBy: input.registeredBy,
114245
114502
  status: "pending",
@@ -114277,11 +114534,11 @@ var TIMESTAMP_KEYS = [
114277
114534
  "published_at"
114278
114535
  ];
114279
114536
  var RESERVED_KEYS = /* @__PURE__ */ new Set(["$lineage", "$writtenBy", "$binary"]);
114280
- function isRecord5(value) {
114537
+ function isRecord6(value) {
114281
114538
  return value !== null && typeof value === "object" && !Array.isArray(value);
114282
114539
  }
114283
114540
  function timestampOf(item) {
114284
- if (!isRecord5(item))
114541
+ if (!isRecord6(item))
114285
114542
  return null;
114286
114543
  for (const key of TIMESTAMP_KEYS) {
114287
114544
  const value = item[key];
@@ -114322,7 +114579,7 @@ function trimSourceData(data, options = {}) {
114322
114579
  total = 0;
114323
114580
  if (Array.isArray(data))
114324
114581
  return trimArray(data, limit2);
114325
- if (isRecord5(data)) {
114582
+ if (isRecord6(data)) {
114326
114583
  const stamped = "$lineage" in data;
114327
114584
  const out = {};
114328
114585
  for (const [key, value] of Object.entries(data)) {
@@ -114354,13 +114611,28 @@ function trimSourceData(data, options = {}) {
114354
114611
  }
114355
114612
  return { data: result, kept, total, truncated: false };
114356
114613
  }
114357
- var SYSTEM_PROMPT = [
114614
+ var GROUNDING = [
114358
114615
  "You answer a question about a person using ONLY the user data provided in the message.",
114359
- "Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer.",
114616
+ "Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer."
114617
+ ];
114618
+ var SYSTEM_PROMPT = [
114619
+ ...GROUNDING,
114360
114620
  "Respond with a single JSON object and nothing else, with exactly these fields:",
114361
114621
  ' "answer": string, the answer to the question, written for the person the data belongs to;',
114362
114622
  ' "evidence": string, a short summary of which parts of the data support the answer.'
114363
114623
  ].join("\n");
114624
+ function shapedSystemPrompt(shape) {
114625
+ return [
114626
+ ...GROUNDING,
114627
+ "Respond with a single JSON object and nothing else, with exactly these fields:",
114628
+ ' "answer": object, holding exactly the fields listed below and no others;',
114629
+ ' "evidence": string, a short summary of which parts of the data support the answer.',
114630
+ 'The "answer" object holds:',
114631
+ ...describeAnswerShape(shape),
114632
+ "Use the declared type for every field: a number field is a JSON number, not a string.",
114633
+ "Do not add fields, do not rename them and do not put prose outside them."
114634
+ ].join("\n");
114635
+ }
114364
114636
  function buildQuestionMessages(input) {
114365
114637
  const sections = input.sources.map((source) => {
114366
114638
  const note = source.total > source.kept ? ` (newest ${source.kept} of ${source.total} items)` : "";
@@ -114378,14 +114650,34 @@ function buildQuestionMessages(input) {
114378
114650
  "## User data",
114379
114651
  ...sections,
114380
114652
  "",
114381
- "Answer the question as a JSON object with the fields answer and evidence."
114653
+ input.answerShape ? "Answer the question as a JSON object with the fields answer and evidence, where answer holds exactly the declared fields." : "Answer the question as a JSON object with the fields answer and evidence."
114382
114654
  ].join("\n");
114383
114655
  return [
114384
- { role: "system", content: SYSTEM_PROMPT },
114656
+ {
114657
+ role: "system",
114658
+ content: input.answerShape ? shapedSystemPrompt(input.answerShape) : SYSTEM_PROMPT
114659
+ },
114385
114660
  { role: "user", content: user }
114386
114661
  ];
114387
114662
  }
114388
- function parseAnswer(content) {
114663
+ function buildShapeRetryMessages(input) {
114664
+ return [
114665
+ ...input.messages,
114666
+ { role: "assistant", content: input.reply },
114667
+ {
114668
+ role: "user",
114669
+ content: [
114670
+ "That reply did not match the required answer shape:",
114671
+ ...input.issues.map((issue2) => `- ${issue2}`),
114672
+ "",
114673
+ 'Answer again as a single JSON object with the fields answer and evidence, where "answer" holds:',
114674
+ ...describeAnswerShape(input.answerShape),
114675
+ "Return the JSON object and nothing else."
114676
+ ].join("\n")
114677
+ }
114678
+ ];
114679
+ }
114680
+ function jsonFramings(content) {
114389
114681
  const candidates = [content.trim()];
114390
114682
  const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(content);
114391
114683
  if (fenced?.[1])
@@ -114395,10 +114687,13 @@ function parseAnswer(content) {
114395
114687
  if (first !== -1 && last2 > first) {
114396
114688
  candidates.push(content.slice(first, last2 + 1));
114397
114689
  }
114398
- for (const candidate of candidates) {
114690
+ return [...new Set(candidates)];
114691
+ }
114692
+ function parseAnswer(content) {
114693
+ for (const candidate of jsonFramings(content)) {
114399
114694
  try {
114400
114695
  const parsed = JSON.parse(candidate);
114401
- if (isRecord5(parsed) && typeof parsed.answer === "string") {
114696
+ if (isRecord6(parsed) && typeof parsed.answer === "string") {
114402
114697
  return {
114403
114698
  answer: parsed.answer,
114404
114699
  evidence: typeof parsed.evidence === "string" ? parsed.evidence : null
@@ -114409,6 +114704,25 @@ function parseAnswer(content) {
114409
114704
  }
114410
114705
  return { answer: content.trim(), evidence: null };
114411
114706
  }
114707
+ function parseShapedAnswer(content) {
114708
+ const found = [];
114709
+ for (const candidate of jsonFramings(content)) {
114710
+ let parsed;
114711
+ try {
114712
+ parsed = JSON.parse(candidate);
114713
+ } catch {
114714
+ continue;
114715
+ }
114716
+ if (!isRecord6(parsed))
114717
+ continue;
114718
+ const evidence = typeof parsed.evidence === "string" ? parsed.evidence : null;
114719
+ if (isRecord6(parsed.answer)) {
114720
+ found.push({ answer: parsed.answer, evidence });
114721
+ }
114722
+ found.push({ answer: parsed, evidence });
114723
+ }
114724
+ return found;
114725
+ }
114412
114726
 
114413
114727
  // ../core/dist/derivatives/inference.js
114414
114728
  var DEFAULT_INFERENCE_BASE_URL = "https://inference.phala.com/v1";
@@ -114435,11 +114749,11 @@ var InferenceRequestError = class extends Error {
114435
114749
  this.retryable = options.retryable;
114436
114750
  }
114437
114751
  };
114438
- function isRecord6(value) {
114752
+ function isRecord7(value) {
114439
114753
  return value !== null && typeof value === "object" && !Array.isArray(value);
114440
114754
  }
114441
114755
  function readUsage(value) {
114442
- if (!isRecord6(value))
114756
+ if (!isRecord7(value))
114443
114757
  return void 0;
114444
114758
  const num2 = (v10) => typeof v10 === "number" ? v10 : void 0;
114445
114759
  const usage = {
@@ -114450,10 +114764,10 @@ function readUsage(value) {
114450
114764
  return usage;
114451
114765
  }
114452
114766
  function readContent(body) {
114453
- if (!isRecord6(body) || !Array.isArray(body.choices))
114767
+ if (!isRecord7(body) || !Array.isArray(body.choices))
114454
114768
  return null;
114455
114769
  const first = body.choices[0];
114456
- if (!isRecord6(first) || !isRecord6(first.message))
114770
+ if (!isRecord7(first) || !isRecord7(first.message))
114457
114771
  return null;
114458
114772
  const index = typeof first.index === "number" ? first.index : 0;
114459
114773
  const field = `choices.${index}.message.content`;
@@ -114462,7 +114776,7 @@ function readContent(body) {
114462
114776
  if (typeof content === "string")
114463
114777
  return { content, field, id: id2 };
114464
114778
  if (Array.isArray(content)) {
114465
- const text = content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").join("");
114779
+ const text = content.map((part) => isRecord7(part) && typeof part.text === "string" ? part.text : "").join("");
114466
114780
  return { content: text, field, id: id2 };
114467
114781
  }
114468
114782
  return null;
@@ -114470,7 +114784,7 @@ function readContent(body) {
114470
114784
  async function readErrorType(response) {
114471
114785
  try {
114472
114786
  const body = await response.json();
114473
- if (isRecord6(body) && isRecord6(body.error)) {
114787
+ if (isRecord7(body) && isRecord7(body.error)) {
114474
114788
  return typeof body.error.type === "string" ? body.error.type : null;
114475
114789
  }
114476
114790
  } catch {
@@ -114578,7 +114892,7 @@ function createOpenAiCompatibleInferenceProvider(options = {}) {
114578
114892
  ok: true,
114579
114893
  result: {
114580
114894
  content,
114581
- usage: readUsage(isRecord6(parsed) ? parsed.usage : void 0),
114895
+ usage: readUsage(isRecord7(parsed) ? parsed.usage : void 0),
114582
114896
  ...receiptId ? { receiptId } : {},
114583
114897
  ...aciIdentity ? { aciIdentity } : {}
114584
114898
  }
@@ -114651,6 +114965,53 @@ function shortError(err2) {
114651
114965
  return `${err2.errorCode}: ${err2.message}`;
114652
114966
  return `compute failed (${err2 instanceof Error ? err2.name : "Error"})`;
114653
114967
  }
114968
+ async function produceAnswer(deps, registration, model, messages) {
114969
+ const ask = (turns2) => withRetries(deps, () => deps.provider.chat({
114970
+ model,
114971
+ messages: turns2,
114972
+ maxTokens: deps.maxTokens
114973
+ }), isRetryableInferenceError);
114974
+ const shape = registration.answerShape;
114975
+ if (!shape) {
114976
+ const reply = await ask(messages);
114977
+ const parsed = parseAnswer(reply.content);
114978
+ return {
114979
+ reply,
114980
+ answer: parsed.answer,
114981
+ evidence: parsed.evidence,
114982
+ answerData: null
114983
+ };
114984
+ }
114985
+ const compiled = compileAnswerShape(shape);
114986
+ let turns = messages;
114987
+ for (let attempt = 0; ; attempt += 1) {
114988
+ const reply = await ask(turns);
114989
+ const candidates = parseShapedAnswer(reply.content);
114990
+ let issues = ["the reply was not a JSON object"];
114991
+ for (const [index, candidate] of candidates.entries()) {
114992
+ const checked = validateAnswerShape(shape, compiled, candidate.answer);
114993
+ if (checked.ok) {
114994
+ return {
114995
+ reply,
114996
+ answer: renderShapedAnswer(shape, checked.value),
114997
+ evidence: candidate.evidence,
114998
+ answerData: checked.value
114999
+ };
115000
+ }
115001
+ if (index === 0)
115002
+ issues = checked.issues;
115003
+ }
115004
+ if (attempt >= 1)
115005
+ break;
115006
+ turns = buildShapeRetryMessages({
115007
+ messages,
115008
+ reply: reply.content,
115009
+ issues,
115010
+ answerShape: shape
115011
+ });
115012
+ }
115013
+ throw new ComputeFailure(`answer did not match the declared shape (fields: ${shape.fields.map((field) => field.name).join(", ")})`, "internal");
115014
+ }
114654
115015
  function collectedAtStamp(now, isTaken) {
114655
115016
  const base = now();
114656
115017
  base.setUTCMilliseconds(0);
@@ -114811,18 +115172,20 @@ async function computeQuestion(questionId, deps) {
114811
115172
  await assertNoLineageCycle(deps, registration, serverOwner, sourceLineage);
114812
115173
  const messages = buildQuestionMessages({
114813
115174
  question: registration.question,
114814
- sources
115175
+ sources,
115176
+ answerShape: registration.answerShape
114815
115177
  });
114816
115178
  const model = registration.model ?? deps.provider.defaultModel;
114817
- const reply = await withRetries(deps, () => deps.provider.chat({ model, messages, maxTokens: deps.maxTokens }), isRetryableInferenceError);
114818
- const parsed = parseAnswer(reply.content);
115179
+ const produced = await produceAnswer(deps, registration, model, messages);
115180
+ const reply = produced.reply;
114819
115181
  const computedAt = now().toISOString();
114820
115182
  const lineageIds = registration.sourceScopes.map((scope) => computeDataPointId(serverOwner, scope));
114821
115183
  const record2 = {
114822
115184
  questionId: registration.questionId,
114823
115185
  question: registration.question,
114824
- answer: parsed.answer,
114825
- evidence: parsed.evidence,
115186
+ answer: produced.answer,
115187
+ ...produced.answerData ? { answerData: produced.answerData } : {},
115188
+ evidence: produced.evidence,
114826
115189
  model,
114827
115190
  computedAt,
114828
115191
  sources: sources.map((source) => ({
@@ -115583,7 +115946,7 @@ var E2eeAttestationError = class extends Error {
115583
115946
  this.name = "E2eeAttestationError";
115584
115947
  }
115585
115948
  };
115586
- function isRecord7(value) {
115949
+ function isRecord8(value) {
115587
115950
  return value !== null && typeof value === "object" && !Array.isArray(value);
115588
115951
  }
115589
115952
  async function sha256Hex2(bytes2) {
@@ -115601,7 +115964,7 @@ async function reportDataFor(keysetDigest, nonce) {
115601
115964
  return sha256Hex2(new TextEncoder().encode(statement));
115602
115965
  }
115603
115966
  function parseReport(body) {
115604
- if (!isRecord7(body) || typeof body.api_version !== "string" || typeof body.workload_keyset_digest !== "string" || !isRecord7(body.attestation) || typeof body.attestation.tee_type !== "string" || typeof body.attestation.report_data !== "string" || !isRecord7(body.attestation.workload_keyset)) {
115967
+ if (!isRecord8(body) || typeof body.api_version !== "string" || typeof body.workload_keyset_digest !== "string" || !isRecord8(body.attestation) || typeof body.attestation.tee_type !== "string" || typeof body.attestation.report_data !== "string" || !isRecord8(body.attestation.workload_keyset)) {
115605
115968
  throw new E2eeAttestationError("malformed_report", "attestation report is missing required fields");
115606
115969
  }
115607
115970
  const keyset = body.attestation.workload_keyset;
@@ -115648,7 +116011,7 @@ async function verifyAciReportBinding(report, nonce, nowSeconds) {
115648
116011
  }
115649
116012
  function selectX25519Key(keyset, expectedKeyId) {
115650
116013
  for (const entry of keyset.e2ee_public_keys) {
115651
- if (!isRecord7(entry) || entry.algo !== E2EE_ALGO_X25519)
116014
+ if (!isRecord8(entry) || entry.algo !== E2EE_ALGO_X25519)
115652
116015
  continue;
115653
116016
  if (typeof entry.public_key !== "string")
115654
116017
  continue;
@@ -133246,7 +133609,8 @@ async function createPsLiteQuestionStore(stateStore) {
133246
133609
  const saved = await stateStore.get(QUESTIONS_KEY);
133247
133610
  const initial = (saved?.version === 1 ? saved.questions : []).map((question) => ({
133248
133611
  ...question,
133249
- recompute: question.recompute ?? "on-change"
133612
+ recompute: question.recompute ?? "on-change",
133613
+ answerShape: question.answerShape ?? null
133250
133614
  }));
133251
133615
  return createInMemoryQuestionStore({
133252
133616
  initial,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/personal-server-ts-server",
3
- "version": "1.14.0",
3
+ "version": "1.15.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.14.0",
48
- "@opendatalabs/personal-server-ts-lite": "1.14.0",
47
+ "@opendatalabs/personal-server-ts-core": "1.15.0",
48
+ "@opendatalabs/personal-server-ts-lite": "1.15.0",
49
49
  "@opendatalabs/vana-sdk": "3.14.0",
50
50
  "better-sqlite3": "^12.11.1",
51
51
  "hono": "^4.12.27",