@opendatalabs/personal-server-ts-core 1.8.0 → 1.10.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.
Files changed (93) hide show
  1. package/dist/api/index.d.ts +19 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +23 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/derivatives/api.d.ts +33 -0
  6. package/dist/derivatives/api.d.ts.map +1 -0
  7. package/dist/derivatives/api.js +216 -0
  8. package/dist/derivatives/api.js.map +1 -0
  9. package/dist/derivatives/compute.d.ts +106 -0
  10. package/dist/derivatives/compute.d.ts.map +1 -0
  11. package/dist/derivatives/compute.js +380 -0
  12. package/dist/derivatives/compute.js.map +1 -0
  13. package/dist/derivatives/e2ee/aad.d.ts +22 -0
  14. package/dist/derivatives/e2ee/aad.d.ts.map +1 -0
  15. package/dist/derivatives/e2ee/aad.js +32 -0
  16. package/dist/derivatives/e2ee/aad.js.map +1 -0
  17. package/dist/derivatives/e2ee/attestation.d.ts +122 -0
  18. package/dist/derivatives/e2ee/attestation.d.ts.map +1 -0
  19. package/dist/derivatives/e2ee/attestation.js +217 -0
  20. package/dist/derivatives/e2ee/attestation.js.map +1 -0
  21. package/dist/derivatives/e2ee/index.d.ts +6 -0
  22. package/dist/derivatives/e2ee/index.d.ts.map +1 -0
  23. package/dist/derivatives/e2ee/index.js +6 -0
  24. package/dist/derivatives/e2ee/index.js.map +1 -0
  25. package/dist/derivatives/e2ee/jcs.d.ts +17 -0
  26. package/dist/derivatives/e2ee/jcs.d.ts.map +1 -0
  27. package/dist/derivatives/e2ee/jcs.js +55 -0
  28. package/dist/derivatives/e2ee/jcs.js.map +1 -0
  29. package/dist/derivatives/e2ee/phala.d.ts +55 -0
  30. package/dist/derivatives/e2ee/phala.d.ts.map +1 -0
  31. package/dist/derivatives/e2ee/phala.js +167 -0
  32. package/dist/derivatives/e2ee/phala.js.map +1 -0
  33. package/dist/derivatives/e2ee/suite.d.ts +56 -0
  34. package/dist/derivatives/e2ee/suite.d.ts.map +1 -0
  35. package/dist/derivatives/e2ee/suite.js +164 -0
  36. package/dist/derivatives/e2ee/suite.js.map +1 -0
  37. package/dist/derivatives/index.d.ts +10 -0
  38. package/dist/derivatives/index.d.ts.map +1 -0
  39. package/dist/derivatives/index.js +10 -0
  40. package/dist/derivatives/index.js.map +1 -0
  41. package/dist/derivatives/inference.d.ts +134 -0
  42. package/dist/derivatives/inference.d.ts.map +1 -0
  43. package/dist/derivatives/inference.js +223 -0
  44. package/dist/derivatives/inference.js.map +1 -0
  45. package/dist/derivatives/prompt.d.ts +64 -0
  46. package/dist/derivatives/prompt.d.ts.map +1 -0
  47. package/dist/derivatives/prompt.js +186 -0
  48. package/dist/derivatives/prompt.js.map +1 -0
  49. package/dist/derivatives/registration.d.ts +53 -0
  50. package/dist/derivatives/registration.d.ts.map +1 -0
  51. package/dist/derivatives/registration.js +163 -0
  52. package/dist/derivatives/registration.js.map +1 -0
  53. package/dist/derivatives/scheduler.d.ts +56 -0
  54. package/dist/derivatives/scheduler.d.ts.map +1 -0
  55. package/dist/derivatives/scheduler.js +151 -0
  56. package/dist/derivatives/scheduler.js.map +1 -0
  57. package/dist/derivatives/store.d.ts +14 -0
  58. package/dist/derivatives/store.d.ts.map +1 -0
  59. package/dist/derivatives/store.js +75 -0
  60. package/dist/derivatives/store.js.map +1 -0
  61. package/dist/derivatives/types.d.ts +72 -0
  62. package/dist/derivatives/types.d.ts.map +1 -0
  63. package/dist/derivatives/types.js +25 -0
  64. package/dist/derivatives/types.js.map +1 -0
  65. package/dist/errors/catalog.d.ts +20 -0
  66. package/dist/errors/catalog.d.ts.map +1 -1
  67. package/dist/errors/catalog.js +26 -0
  68. package/dist/errors/catalog.js.map +1 -1
  69. package/dist/schemas/server-config.d.ts +14 -0
  70. package/dist/schemas/server-config.d.ts.map +1 -1
  71. package/dist/schemas/server-config.js +34 -0
  72. package/dist/schemas/server-config.js.map +1 -1
  73. package/dist/sync/workers/download.d.ts +14 -0
  74. package/dist/sync/workers/download.d.ts.map +1 -1
  75. package/dist/sync/workers/download.js +23 -0
  76. package/dist/sync/workers/download.js.map +1 -1
  77. package/dist/test-utils/e2ee-fixtures.d.ts +94 -0
  78. package/dist/test-utils/e2ee-fixtures.d.ts.map +1 -0
  79. package/dist/test-utils/e2ee-fixtures.js +116 -0
  80. package/dist/test-utils/e2ee-fixtures.js.map +1 -0
  81. package/dist/test-utils/e2ee-gateway.d.ts +61 -0
  82. package/dist/test-utils/e2ee-gateway.d.ts.map +1 -0
  83. package/dist/test-utils/e2ee-gateway.js +248 -0
  84. package/dist/test-utils/e2ee-gateway.js.map +1 -0
  85. package/dist/test-utils/index.d.ts +3 -0
  86. package/dist/test-utils/index.d.ts.map +1 -1
  87. package/dist/test-utils/index.js +3 -0
  88. package/dist/test-utils/index.js.map +1 -1
  89. package/dist/test-utils/memory-storage.d.ts +12 -0
  90. package/dist/test-utils/memory-storage.d.ts.map +1 -0
  91. package/dist/test-utils/memory-storage.js +146 -0
  92. package/dist/test-utils/memory-storage.js.map +1 -0
  93. package/package.json +5 -1
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Inference provider adapter for the derivative compute layer.
3
+ *
4
+ * One implementation: OpenAI-compatible chat completions over `fetch`, so it
5
+ * runs unchanged in Node and in the browser (PS-Lite). In production the
6
+ * base URL is the Vana inference relay, which holds the provider key; the
7
+ * optional API key header exists for local development against a provider
8
+ * directly.
9
+ *
10
+ * E2EE SEAM: the Phala confidential-inference E2EE v2 protocol encrypts each
11
+ * `messages[i].content` to the gateway's attested public key and adds the
12
+ * headers X-E2EE-Version, X-Client-Pub-Key, X-Model-Pub-Key, X-E2EE-Nonce
13
+ * and X-E2EE-Timestamp; the response content comes back encrypted to the
14
+ * client key. `InferenceRequestEncryption` below is the hook, implemented
15
+ * by `createPhalaE2eeEncryption` in ./e2ee. This module only knows the
16
+ * shape: encrypt the outgoing messages, decrypt the one response field.
17
+ */
18
+ export const DEFAULT_INFERENCE_BASE_URL = "https://inference.phala.com/v1";
19
+ export const DEFAULT_INFERENCE_MODEL = "z-ai/glm-5.2";
20
+ export const DEFAULT_INFERENCE_TIMEOUT_MS = 120_000;
21
+ export const DEFAULT_INFERENCE_MAX_TOKENS = 2_048;
22
+ export const DEFAULT_INFERENCE_REQUEST_FIELDS = {
23
+ provider: { aci_verified: true, zdr: true },
24
+ };
25
+ /** Thrown for a non-2xx or malformed provider reply. Carries no prompt text. */
26
+ export class InferenceRequestError extends Error {
27
+ status;
28
+ /** OpenAI-style `error.type` of the rejection, when the body had one. */
29
+ errorType;
30
+ /**
31
+ * Explicit retry hint. When undefined the compute layer falls back to the
32
+ * status: no response, 429 or 5xx are retried.
33
+ */
34
+ retryable;
35
+ constructor(message, status, options = {}) {
36
+ super(message);
37
+ this.status = status;
38
+ this.name = "InferenceRequestError";
39
+ this.errorType = options.errorType ?? null;
40
+ this.retryable = options.retryable;
41
+ }
42
+ }
43
+ function isRecord(value) {
44
+ return value !== null && typeof value === "object" && !Array.isArray(value);
45
+ }
46
+ function readUsage(value) {
47
+ if (!isRecord(value))
48
+ return undefined;
49
+ const num = (v) => (typeof v === "number" ? v : undefined);
50
+ const usage = {
51
+ promptTokens: num(value.prompt_tokens),
52
+ completionTokens: num(value.completion_tokens),
53
+ totalTokens: num(value.total_tokens),
54
+ };
55
+ return usage;
56
+ }
57
+ function readContent(body) {
58
+ if (!isRecord(body) || !Array.isArray(body.choices))
59
+ return null;
60
+ const first = body.choices[0];
61
+ if (!isRecord(first) || !isRecord(first.message))
62
+ return null;
63
+ const index = typeof first.index === "number" ? first.index : 0;
64
+ const field = `choices.${index}.message.content`;
65
+ const id = typeof body.id === "string" ? body.id : "";
66
+ const content = first.message.content;
67
+ if (typeof content === "string")
68
+ return { content, field, id };
69
+ // Some providers return content parts.
70
+ if (Array.isArray(content)) {
71
+ const text = content
72
+ .map((part) => isRecord(part) && typeof part.text === "string" ? part.text : "")
73
+ .join("");
74
+ return { content: text, field, id };
75
+ }
76
+ return null;
77
+ }
78
+ /** OpenAI-style `{ error: { type } }` of a rejection; never its message. */
79
+ async function readErrorType(response) {
80
+ try {
81
+ const body = await response.json();
82
+ if (isRecord(body) && isRecord(body.error)) {
83
+ return typeof body.error.type === "string" ? body.error.type : null;
84
+ }
85
+ }
86
+ catch {
87
+ // Not JSON: no type to report.
88
+ }
89
+ return null;
90
+ }
91
+ export function createOpenAiCompatibleInferenceProvider(options = {}) {
92
+ const base = (options.baseUrl ?? DEFAULT_INFERENCE_BASE_URL).replace(/\/+$/, "");
93
+ const defaultModel = options.model ?? DEFAULT_INFERENCE_MODEL;
94
+ const timeoutMs = options.timeoutMs ?? DEFAULT_INFERENCE_TIMEOUT_MS;
95
+ const doFetch = options.fetch ?? fetch;
96
+ const requestFields = options.requestFields ?? DEFAULT_INFERENCE_REQUEST_FIELDS;
97
+ const encryption = options.encryption;
98
+ /** One send; `rejected` reports a non-2xx so the caller may retry once. */
99
+ async function send(input) {
100
+ const headers = new Headers({ "Content-Type": "application/json" });
101
+ if (options.apiKey) {
102
+ headers.set("Authorization", `Bearer ${options.apiKey}`);
103
+ }
104
+ const model = input.model || defaultModel;
105
+ let messages = input.messages;
106
+ let encrypted = null;
107
+ if (encryption) {
108
+ encrypted = await encryption.encryptRequest({
109
+ model,
110
+ messages,
111
+ headers,
112
+ });
113
+ messages = encrypted.messages;
114
+ }
115
+ const body = {
116
+ ...requestFields,
117
+ model,
118
+ messages,
119
+ max_tokens: input.maxTokens ?? DEFAULT_INFERENCE_MAX_TOKENS,
120
+ };
121
+ let response;
122
+ try {
123
+ response = await doFetch(`${base}/chat/completions`, {
124
+ method: "POST",
125
+ headers,
126
+ body: JSON.stringify(body),
127
+ signal: AbortSignal.timeout(timeoutMs),
128
+ });
129
+ }
130
+ catch (err) {
131
+ // Transport failure: no status, no body. The message is the error
132
+ // class name only; it never carries request contents.
133
+ const name = err instanceof Error ? err.name : "Error";
134
+ throw new InferenceRequestError(`inference request failed before a response (${name})`, null);
135
+ }
136
+ if (!response.ok) {
137
+ const errorType = await readErrorType(response);
138
+ const retry = (await encryption?.onRejected?.({
139
+ status: response.status,
140
+ errorType,
141
+ headers: response.headers,
142
+ })) === true;
143
+ return {
144
+ ok: false,
145
+ retry,
146
+ error: new InferenceRequestError(`inference request failed with status ${response.status}${errorType ? ` (${errorType})` : ""}`, response.status, { errorType }),
147
+ };
148
+ }
149
+ let parsed;
150
+ try {
151
+ parsed = await response.json();
152
+ }
153
+ catch {
154
+ throw new InferenceRequestError("inference response was not JSON", response.status);
155
+ }
156
+ const choice = readContent(parsed);
157
+ if (choice === null) {
158
+ throw new InferenceRequestError("inference response carried no assistant content", response.status);
159
+ }
160
+ let content = choice.content;
161
+ if (encrypted) {
162
+ // Fail closed: a reply that is not valid ciphertext for this request
163
+ // is an error, never used as plaintext.
164
+ content = await encrypted.decryptResponse({
165
+ content,
166
+ field: choice.field,
167
+ id: choice.id,
168
+ headers: response.headers,
169
+ });
170
+ }
171
+ if (content.trim() === "") {
172
+ // An empty reply must never become a "ready" derivative.
173
+ throw new InferenceRequestError("inference response carried no assistant content", response.status);
174
+ }
175
+ const receiptId = response.headers.get("x-receipt-id") ?? undefined;
176
+ const aciIdentity = response.headers.get("x-aci-identity") ?? undefined;
177
+ return {
178
+ ok: true,
179
+ result: {
180
+ content,
181
+ usage: readUsage(isRecord(parsed) ? parsed.usage : undefined),
182
+ ...(receiptId ? { receiptId } : {}),
183
+ ...(aciIdentity ? { aciIdentity } : {}),
184
+ },
185
+ };
186
+ }
187
+ return {
188
+ defaultModel,
189
+ async chat(input) {
190
+ const first = await send(input);
191
+ if (first.ok)
192
+ return first.result;
193
+ if (!first.retry)
194
+ throw first.error;
195
+ // The encryption asked for one re-send (fresh key, fresh nonce).
196
+ const second = await send(input);
197
+ if (second.ok)
198
+ return second.result;
199
+ throw second.error;
200
+ },
201
+ };
202
+ }
203
+ /** Test double: records calls, answers with a fixed JSON object by default. */
204
+ export function createFakeInferenceProvider(options = {}) {
205
+ const calls = [];
206
+ return {
207
+ defaultModel: options.model ?? "fake-model",
208
+ calls,
209
+ async chat(input) {
210
+ calls.push(input);
211
+ if (options.respond)
212
+ return options.respond(input, calls.length - 1);
213
+ return {
214
+ content: JSON.stringify({
215
+ answer: "fake answer",
216
+ evidence: "fake evidence",
217
+ }),
218
+ receiptId: "fake-receipt",
219
+ };
220
+ },
221
+ };
222
+ }
223
+ //# sourceMappingURL=inference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inference.js","sourceRoot":"","sources":["../../src/derivatives/inference.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Prompt assembly for a question: the source scopes' latest local records,
3
+ * trimmed newest-first to a bounded number of items each, framed by a system
4
+ * prompt that pins the model to the provided data and to a JSON answer.
5
+ *
6
+ * Nothing here leaves the Personal Server except through the inference
7
+ * provider call the compute step makes.
8
+ */
9
+ import type { InferenceMessage } from "./inference.js";
10
+ export declare const DEFAULT_MAX_SOURCE_ITEMS = 50;
11
+ /** Upper bound on the serialized size of one source's trimmed data. */
12
+ export declare const DEFAULT_MAX_SOURCE_CHARS = 200000;
13
+ /**
14
+ * Newest first. Items that carry a timestamp sort by it; items that do not
15
+ * keep their relative order after the timestamped ones, reversed, on the
16
+ * assumption that a connector appends newest last. Stable.
17
+ */
18
+ export declare function sortNewestFirst<T>(items: readonly T[]): T[];
19
+ export interface TrimResult {
20
+ data: unknown;
21
+ /** Items kept / items present, summed over every array that was trimmed. */
22
+ kept: number;
23
+ total: number;
24
+ /** True when the char cap cut the serialization. */
25
+ truncated: boolean;
26
+ }
27
+ /**
28
+ * The trim rule. A record whose `data` is an array is trimmed to the newest
29
+ * `maxItems`; a record object trims every top-level array value the same
30
+ * way and keeps its other keys; reserved server keys are dropped. When the
31
+ * serialized result still exceeds `maxChars`, items are dropped from the
32
+ * old end until it fits (at least one is kept), and as a last resort the
33
+ * serialization itself is cut at `maxChars`.
34
+ */
35
+ export declare function trimSourceData(data: unknown, options?: {
36
+ maxItems?: number;
37
+ maxChars?: number;
38
+ }): TrimResult;
39
+ export interface PromptSource {
40
+ scope: string;
41
+ collectedAt: string;
42
+ version: number;
43
+ data: unknown;
44
+ kept: number;
45
+ total: number;
46
+ truncated: boolean;
47
+ }
48
+ export declare const SYSTEM_PROMPT: string;
49
+ /** Assemble the chat messages for one compute. */
50
+ export declare function buildQuestionMessages(input: {
51
+ question: string;
52
+ sources: readonly PromptSource[];
53
+ }): InferenceMessage[];
54
+ export interface ParsedAnswer {
55
+ answer: string;
56
+ evidence: string | null;
57
+ }
58
+ /**
59
+ * Read the model's JSON answer. Tolerates a fenced code block or leading
60
+ * prose around the object; falls back to the whole text as the answer when
61
+ * no object can be parsed (the answer is still the model's, just unframed).
62
+ */
63
+ export declare function parseAnswer(content: string): ParsedAnswer;
64
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/derivatives/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,SAAU,CAAC;AAyChD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAW3D;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,EACb,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACrD,UAAU,CA+CZ;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,aAAa,QAMd,CAAC;AAEb,kDAAkD;AAClD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;CAClC,GAAG,gBAAgB,EAAE,CA0BrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAwBzD"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Prompt assembly for a question: the source scopes' latest local records,
3
+ * trimmed newest-first to a bounded number of items each, framed by a system
4
+ * prompt that pins the model to the provided data and to a JSON answer.
5
+ *
6
+ * Nothing here leaves the Personal Server except through the inference
7
+ * provider call the compute step makes.
8
+ */
9
+ export const DEFAULT_MAX_SOURCE_ITEMS = 50;
10
+ /** Upper bound on the serialized size of one source's trimmed data. */
11
+ export const DEFAULT_MAX_SOURCE_CHARS = 200_000;
12
+ /** Keys a record commonly carries its own timestamp under, in priority order. */
13
+ const TIMESTAMP_KEYS = [
14
+ "collectedAt",
15
+ "updatedAt",
16
+ "updated_at",
17
+ "update_time",
18
+ "createdAt",
19
+ "created_at",
20
+ "create_time",
21
+ "timestamp",
22
+ "time",
23
+ "date",
24
+ "publishedAt",
25
+ "published_at",
26
+ ];
27
+ /** Server-stamped envelope keys that are not user data. */
28
+ const RESERVED_KEYS = new Set(["$lineage", "$writtenBy", "$binary"]);
29
+ function isRecord(value) {
30
+ return value !== null && typeof value === "object" && !Array.isArray(value);
31
+ }
32
+ function timestampOf(item) {
33
+ if (!isRecord(item))
34
+ return null;
35
+ for (const key of TIMESTAMP_KEYS) {
36
+ const value = item[key];
37
+ if (typeof value === "number" && Number.isFinite(value)) {
38
+ // Seconds vs milliseconds: anything below 1e12 is taken as seconds.
39
+ return value < 1e12 ? value * 1000 : value;
40
+ }
41
+ if (typeof value === "string") {
42
+ const parsed = Date.parse(value);
43
+ if (!Number.isNaN(parsed))
44
+ return parsed;
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ /**
50
+ * Newest first. Items that carry a timestamp sort by it; items that do not
51
+ * keep their relative order after the timestamped ones, reversed, on the
52
+ * assumption that a connector appends newest last. Stable.
53
+ */
54
+ export function sortNewestFirst(items) {
55
+ const indexed = items.map((item, index) => ({
56
+ item,
57
+ index,
58
+ at: timestampOf(item),
59
+ }));
60
+ const dated = indexed
61
+ .filter((entry) => entry.at !== null)
62
+ .sort((a, b) => b.at - a.at || b.index - a.index);
63
+ const undated = indexed.filter((entry) => entry.at === null).reverse();
64
+ return [...dated, ...undated].map((entry) => entry.item);
65
+ }
66
+ /**
67
+ * The trim rule. A record whose `data` is an array is trimmed to the newest
68
+ * `maxItems`; a record object trims every top-level array value the same
69
+ * way and keeps its other keys; reserved server keys are dropped. When the
70
+ * serialized result still exceeds `maxChars`, items are dropped from the
71
+ * old end until it fits (at least one is kept), and as a last resort the
72
+ * serialization itself is cut at `maxChars`.
73
+ */
74
+ export function trimSourceData(data, options = {}) {
75
+ const maxItems = Math.max(1, options.maxItems ?? DEFAULT_MAX_SOURCE_ITEMS);
76
+ const maxChars = Math.max(1, options.maxChars ?? DEFAULT_MAX_SOURCE_CHARS);
77
+ let kept = 0;
78
+ let total = 0;
79
+ const trimArray = (items, limit) => {
80
+ total += items.length;
81
+ const sorted = sortNewestFirst(items).slice(0, limit);
82
+ kept += sorted.length;
83
+ return sorted;
84
+ };
85
+ const build = (limit) => {
86
+ kept = 0;
87
+ total = 0;
88
+ if (Array.isArray(data))
89
+ return trimArray(data, limit);
90
+ if (isRecord(data)) {
91
+ const out = {};
92
+ for (const [key, value] of Object.entries(data)) {
93
+ if (RESERVED_KEYS.has(key))
94
+ continue;
95
+ out[key] = Array.isArray(value) ? trimArray(value, limit) : value;
96
+ }
97
+ return out;
98
+ }
99
+ return data;
100
+ };
101
+ let limit = maxItems;
102
+ let result = build(limit);
103
+ let text = JSON.stringify(result) ?? "null";
104
+ // Shrink the item budget while the serialization is over the cap. Each
105
+ // pass at least halves the budget, so this ends in O(log maxItems) steps.
106
+ while (text.length > maxChars && limit > 1) {
107
+ limit = Math.max(1, Math.floor(limit / 2));
108
+ result = build(limit);
109
+ text = JSON.stringify(result) ?? "null";
110
+ }
111
+ if (text.length > maxChars) {
112
+ return {
113
+ data: `${text.slice(0, maxChars)}...[truncated]`,
114
+ kept,
115
+ total,
116
+ truncated: true,
117
+ };
118
+ }
119
+ return { data: result, kept, total, truncated: false };
120
+ }
121
+ export const SYSTEM_PROMPT = [
122
+ "You answer a question about a person using ONLY the user data provided in the message.",
123
+ "Do not use outside knowledge and do not guess; if the data does not support an answer, say so in the answer.",
124
+ "Respond with a single JSON object and nothing else, with exactly these fields:",
125
+ ' "answer": string, the answer to the question, written for the person the data belongs to;',
126
+ ' "evidence": string, a short summary of which parts of the data support the answer.',
127
+ ].join("\n");
128
+ /** Assemble the chat messages for one compute. */
129
+ export function buildQuestionMessages(input) {
130
+ const sections = input.sources.map((source) => {
131
+ const note = source.total > source.kept
132
+ ? ` (newest ${source.kept} of ${source.total} items)`
133
+ : "";
134
+ const cut = source.truncated ? " (truncated)" : "";
135
+ return [
136
+ `### Scope: ${source.scope}${note}${cut}`,
137
+ `Collected at: ${source.collectedAt}`,
138
+ JSON.stringify(source.data),
139
+ ].join("\n");
140
+ });
141
+ const user = [
142
+ "## Question",
143
+ input.question,
144
+ "",
145
+ "## User data",
146
+ ...sections,
147
+ "",
148
+ "Answer the question as a JSON object with the fields answer and evidence.",
149
+ ].join("\n");
150
+ return [
151
+ { role: "system", content: SYSTEM_PROMPT },
152
+ { role: "user", content: user },
153
+ ];
154
+ }
155
+ /**
156
+ * Read the model's JSON answer. Tolerates a fenced code block or leading
157
+ * prose around the object; falls back to the whole text as the answer when
158
+ * no object can be parsed (the answer is still the model's, just unframed).
159
+ */
160
+ export function parseAnswer(content) {
161
+ const candidates = [content.trim()];
162
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(content);
163
+ if (fenced?.[1])
164
+ candidates.unshift(fenced[1].trim());
165
+ const first = content.indexOf("{");
166
+ const last = content.lastIndexOf("}");
167
+ if (first !== -1 && last > first) {
168
+ candidates.push(content.slice(first, last + 1));
169
+ }
170
+ for (const candidate of candidates) {
171
+ try {
172
+ const parsed = JSON.parse(candidate);
173
+ if (isRecord(parsed) && typeof parsed.answer === "string") {
174
+ return {
175
+ answer: parsed.answer,
176
+ evidence: typeof parsed.evidence === "string" ? parsed.evidence : null,
177
+ };
178
+ }
179
+ }
180
+ catch {
181
+ // try the next framing
182
+ }
183
+ }
184
+ return { answer: content.trim(), evidence: null };
185
+ }
186
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/derivatives/prompt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Question registration: input validation, the naming rule (shared with the
3
+ * lineage write path) and the cycle guard that keeps recompute-on-refresh
4
+ * bounded.
5
+ */
6
+ import type { QuestionRegisteredBy, QuestionRegistration, QuestionStore } from "./types.js";
7
+ export declare const MAX_QUESTION_SOURCE_SCOPES = 16;
8
+ export declare const MAX_QUESTION_CHARS = 8000;
9
+ export declare const MAX_MODEL_CHARS = 128;
10
+ /** Unvalidated scope strings echoed in 400 details are cut to this. */
11
+ export declare const MAX_ECHOED_SCOPE_CHARS = 128;
12
+ export interface ParsedQuestionInput {
13
+ derivedScope: string;
14
+ sourceScopes: string[];
15
+ question: string;
16
+ model: string | null;
17
+ }
18
+ /**
19
+ * Validate the POST body of a question registration. Throws 400
20
+ * DERIVATIVE_QUESTION_INVALID on shape errors and 400
21
+ * LINEAGE_SCOPE_UNDER_SOURCE_PREFIX when the derived scope shares its
22
+ * first segment with a source (the lineage naming rule, applied up front
23
+ * so a registration that could never be written is refused here).
24
+ */
25
+ export declare function parseQuestionInput(body: unknown): ParsedQuestionInput;
26
+ /**
27
+ * Recompute-on-refresh follows edges source -> derived. A registration whose
28
+ * derived scope can reach one of its own sources through other
29
+ * registrations would recompute forever (A from B, B from A). Refuse it.
30
+ * Returns the offending path (derived scope back to itself) or null.
31
+ */
32
+ export declare function findDerivationCycle(candidate: Pick<QuestionRegistration, "derivedScope" | "sourceScopes">, existing: readonly Pick<QuestionRegistration, "derivedScope" | "sourceScopes">[]): string[] | null;
33
+ export interface CreateQuestionRegistrationInput {
34
+ body: unknown;
35
+ registeredBy: QuestionRegisteredBy;
36
+ store: QuestionStore;
37
+ questionId: string;
38
+ now: () => Date;
39
+ }
40
+ /**
41
+ * Validate, guard against cycles and persist a registration in `pending`.
42
+ * The caller has already authorized the write on `derivedScope`.
43
+ */
44
+ export declare function createQuestionRegistration(input: CreateQuestionRegistrationInput): Promise<QuestionRegistration>;
45
+ /**
46
+ * Consent check for a builder question: the sources feed a prompt whose
47
+ * answer the builder reads, so every source scope must be covered by a READ
48
+ * entry of the builder's grant (bare entries; `write:` entries confer
49
+ * nothing). Returns the uncovered scopes; empty = all covered. The system
50
+ * prompt is not a security boundary, this is.
51
+ */
52
+ export declare function uncoveredSourceScopes(sourceScopes: readonly string[], grantScopes: readonly string[] | undefined): string[];
53
+ //# sourceMappingURL=registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/derivatives/registration.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,KAAK,EACV,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AACxC,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,uEAAuE;AACvE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAK1C,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAyBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB,CA8DrE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,IAAI,CAAC,oBAAoB,EAAE,cAAc,GAAG,cAAc,CAAC,EACtE,QAAQ,EAAE,SAAS,IAAI,CACrB,oBAAoB,EACpB,cAAc,GAAG,cAAc,CAChC,EAAE,GACF,MAAM,EAAE,GAAG,IAAI,CA+BjB;AAED,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,oBAAoB,CAAC;IACnC,KAAK,EAAE,aAAa,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,+BAA+B,GACrC,OAAO,CAAC,oBAAoB,CAAC,CA2B/B;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACzC,MAAM,EAAE,CAOV"}