xmemory 2.1.2 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -0
- package/dist/client.d.ts +6 -2
- package/dist/client.js +77 -6
- package/dist/index.d.ts +4 -2
- package/dist/instance.d.ts +27 -1
- package/dist/instance.js +51 -0
- package/dist/types.d.ts +259 -3
- package/dist/types.js +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -183,6 +183,109 @@ const tools = desc.asOpenaiTools(); // OpenAI function-calling format
|
|
|
183
183
|
|
|
184
184
|
Results are cached for 5 minutes. Call `inst.clearDescribeCache()` to force a refresh.
|
|
185
185
|
|
|
186
|
+
## Schema evolution
|
|
187
|
+
|
|
188
|
+
Schemas can change after creation. xmemory supports **safe, data-preserving
|
|
189
|
+
migrations** (rename / remove / type change) driven by structured migration
|
|
190
|
+
ops, plus a **suggestion engine** that proposes improvements from real read
|
|
191
|
+
traffic. This is purely additive — existing methods are unchanged.
|
|
192
|
+
|
|
193
|
+
See the [Schema evolution section of the API reference](https://xmemory.ai/api/#schema-evolution)
|
|
194
|
+
for the conceptual model, and the [TypeScript guide](https://xmemory.ai/typescript/)
|
|
195
|
+
for full walkthroughs.
|
|
196
|
+
|
|
197
|
+
### Suggestion-engine flow (review → decide → apply)
|
|
198
|
+
|
|
199
|
+
The engine surfaces a single rolling proposal per instance. The minimum flow is
|
|
200
|
+
three calls — review, decide (in bulk), apply:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { XmemoryClient, type DecisionInput } from "xmemory";
|
|
204
|
+
|
|
205
|
+
const xm = new XmemoryClient({ apiKey: "..." });
|
|
206
|
+
const inst = xm.instance("<instance-id>");
|
|
207
|
+
|
|
208
|
+
// 1. Review — get the proposal + its concurrency token.
|
|
209
|
+
const review = await inst.reviewSuggestions();
|
|
210
|
+
if (review.status === "evolution_in_progress") {
|
|
211
|
+
console.log(`A migration is in flight; retry in ${review.retry_after_seconds}s`);
|
|
212
|
+
} else if (review.proposal) {
|
|
213
|
+
const proposal = review.proposal;
|
|
214
|
+
for (const item of proposal.items) {
|
|
215
|
+
console.log(item.item_fingerprint, item.rationale, item.op);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 2. Decide — accept / reject / defer per item, in one batch.
|
|
219
|
+
const decisions: DecisionInput[] = proposal.items.map((item) => ({
|
|
220
|
+
item_fingerprint: item.item_fingerprint,
|
|
221
|
+
decision: "accept",
|
|
222
|
+
}));
|
|
223
|
+
const decided = await inst.decideSuggestions(proposal.proposal_version, decisions);
|
|
224
|
+
|
|
225
|
+
// 3. Apply — commit accepted decisions as one migration.
|
|
226
|
+
const applied = await inst.applyPendingDecisions(decided.next_proposal_version);
|
|
227
|
+
console.log(applied.status, applied.summary); // e.g. "ok" "added 1 field"
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
When `status === "evolution_in_progress"`, back off for `retry_after_seconds`
|
|
232
|
+
and retry instead of blocking.
|
|
233
|
+
|
|
234
|
+
### Direct migration flow (enhance → dry-run → update)
|
|
235
|
+
|
|
236
|
+
Drive a migration yourself — ask the server to *enhance* the current schema,
|
|
237
|
+
preview the DDL, then apply it:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { XmemoryClient, SchemaType } from "xmemory";
|
|
241
|
+
import yaml from "js-yaml";
|
|
242
|
+
|
|
243
|
+
const xm = new XmemoryClient({ apiKey: "..." });
|
|
244
|
+
const current = (await xm.admin.getInstanceSchema("<instance-id>")).data_schema;
|
|
245
|
+
|
|
246
|
+
// 1. Enhance — new schema + an executor-ready migration plan.
|
|
247
|
+
const enhanced = await xm.admin.enhanceSchema(
|
|
248
|
+
"<cluster-id>",
|
|
249
|
+
"Rename Person.mail to Person.email.",
|
|
250
|
+
yaml.dump(current),
|
|
251
|
+
);
|
|
252
|
+
console.log(enhanced.summary, enhanced.migration_plan?.ops);
|
|
253
|
+
|
|
254
|
+
const newYaml = yaml.dump(enhanced.data_schema);
|
|
255
|
+
|
|
256
|
+
// 2. Dry-run — preview the DDL without applying anything.
|
|
257
|
+
const preview = await xm.admin.dryRunMigration("<instance-id>", newYaml, SchemaType.YML, {
|
|
258
|
+
migrationPlan: enhanced.migration_plan ?? undefined,
|
|
259
|
+
});
|
|
260
|
+
console.log(preview.statements);
|
|
261
|
+
|
|
262
|
+
// 3. Update — apply. confirmDestructive is required for ops that drop data.
|
|
263
|
+
const info = await xm.admin.updateInstanceSchema("<instance-id>", newYaml, SchemaType.YML, {
|
|
264
|
+
migrationPlan: enhanced.migration_plan ?? undefined,
|
|
265
|
+
confirmDestructive: false,
|
|
266
|
+
});
|
|
267
|
+
console.log(info.migration_id, info.prior_version, "->", info.new_version);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Migration history
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const page = await xm.admin.listMigrations("<instance-id>", { limit: 20 });
|
|
274
|
+
for (const record of page.items) {
|
|
275
|
+
console.log(record.id, record.source, record.prior_version, "->", record.new_version);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const detail = await xm.admin.getMigration("<instance-id>", "<migration-id>", { includeYaml: true });
|
|
279
|
+
console.log(detail.yaml_before, detail.yaml_after);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Migration ops are exported as discriminated-union types (`MigrationPlan`,
|
|
283
|
+
`MigrationOp`, `AddField`, `RenameField`, `RemoveObject`, …) keyed on `op_type`.
|
|
284
|
+
`ProposalItem.op` and `MigrationRecord.ops` are raw dicts for forward
|
|
285
|
+
compatibility — narrow them to `MigrationOp` when needed.
|
|
286
|
+
|
|
287
|
+
Runnable end-to-end examples live in [`examples/`](examples/).
|
|
288
|
+
|
|
186
289
|
## Error handling
|
|
187
290
|
|
|
188
291
|
All errors throw `XmemoryAPIError`. Health check failures throw `XmemoryHealthCheckError` (a subclass).
|
|
@@ -207,6 +310,23 @@ try {
|
|
|
207
310
|
}
|
|
208
311
|
```
|
|
209
312
|
|
|
313
|
+
`XmemoryAPIError` carries `status` (HTTP status), `code` (structured error code,
|
|
314
|
+
when the server returned one), and `details`. The schema-evolution endpoints
|
|
315
|
+
return codes you can pattern match on via `.code` — for example
|
|
316
|
+
`stale_proposal_version`, `dependency_closure_failed`,
|
|
317
|
+
`destructive_confirmation_required`, `non_additive_change_requires_plan`,
|
|
318
|
+
`stale_schema_version`, `migration_not_found`, `instance_not_initialised`:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
try {
|
|
322
|
+
await inst.applyPendingDecisions(token);
|
|
323
|
+
} catch (e) {
|
|
324
|
+
if (e instanceof XmemoryAPIError && e.code === "stale_proposal_version") {
|
|
325
|
+
const review = await inst.reviewSuggestions(); // re-review and retry
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
210
330
|
## All timeouts are per-request
|
|
211
331
|
|
|
212
332
|
Every method accepts an optional `timeoutMs` in its options bag, overriding the client default.
|
package/dist/client.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* XmemoryClient — main entry point for the xmemory API.
|
|
3
3
|
*/
|
|
4
4
|
import { InstanceHandle } from "./instance.js";
|
|
5
|
-
import { type ClusterInfo, type CreateInstanceOptions, type GenerateSchemaOptions, type GenerateSchemaResult, type InstanceInfo, type InstanceSchemaInfo, type RequestOptions, type SchemaTypeValue, type XmemoryClientOptions } from "./types.js";
|
|
5
|
+
import { type ClusterInfo, type CreateInstanceOptions, type DryRunMigrationOptions, type DryRunResult, type EnhanceSchemaResult, type GenerateSchemaOptions, type GenerateSchemaResult, type GetMigrationOptions, type InstanceInfo, type InstanceSchemaInfo, type ListMigrationsOptions, type ListMigrationsResult, type MigrationRecord, type RequestOptions, type SchemaTypeValue, type UpdateInstanceSchemaOptions, type XmemoryClientOptions } from "./types.js";
|
|
6
6
|
export interface AdminNamespace {
|
|
7
7
|
listClusters(options?: RequestOptions & {
|
|
8
8
|
ids?: string[];
|
|
@@ -15,9 +15,13 @@ export interface AdminNamespace {
|
|
|
15
15
|
getInstance(instanceId: string, options?: RequestOptions): Promise<InstanceInfo>;
|
|
16
16
|
deleteInstance(instanceId: string, options?: RequestOptions): Promise<string[]>;
|
|
17
17
|
getInstanceSchema(instanceId: string, options?: RequestOptions): Promise<InstanceSchemaInfo>;
|
|
18
|
-
updateInstanceSchema(instanceId: string, schemaText: string, schemaType: SchemaTypeValue, options?:
|
|
18
|
+
updateInstanceSchema(instanceId: string, schemaText: string, schemaType: SchemaTypeValue, options?: UpdateInstanceSchemaOptions): Promise<InstanceInfo>;
|
|
19
19
|
updateInstanceMetadata(instanceId: string, name: string, description: string, options?: RequestOptions): Promise<InstanceInfo>;
|
|
20
20
|
generateSchema(clusterId: string, schemaDescription: string, options?: GenerateSchemaOptions): Promise<GenerateSchemaResult>;
|
|
21
|
+
enhanceSchema(clusterId: string, schemaDescription: string, currentYmlSchema: string, options?: RequestOptions): Promise<EnhanceSchemaResult>;
|
|
22
|
+
dryRunMigration(instanceId: string, schemaText: string, schemaType: SchemaTypeValue, options?: DryRunMigrationOptions): Promise<DryRunResult>;
|
|
23
|
+
listMigrations(instanceId: string, options?: ListMigrationsOptions): Promise<ListMigrationsResult>;
|
|
24
|
+
getMigration(instanceId: string, migrationId: string, options?: GetMigrationOptions): Promise<MigrationRecord>;
|
|
21
25
|
}
|
|
22
26
|
export declare class XmemoryClient {
|
|
23
27
|
private readonly _baseUrl;
|
package/dist/client.js
CHANGED
|
@@ -10,6 +10,33 @@ const RESET = "\x1b[0m";
|
|
|
10
10
|
function deprecationWarning(message) {
|
|
11
11
|
console.warn(`${ORANGE}[xmemory] DEPRECATION: ${message}${RESET}`);
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Pull a structured error out of an error body. Handles the schema-evolution
|
|
15
|
+
* shape (`{status: "error", error_type, error_message, details}`, where
|
|
16
|
+
* `error_type` is the code) and the standard `{errors: [{code, message}]}`
|
|
17
|
+
* envelope.
|
|
18
|
+
*/
|
|
19
|
+
function extractStructuredError(payload) {
|
|
20
|
+
if (typeof payload !== "object" || payload === null)
|
|
21
|
+
return {};
|
|
22
|
+
const p = payload;
|
|
23
|
+
if (p.status === "error" && typeof p.error_type === "string") {
|
|
24
|
+
return {
|
|
25
|
+
code: p.error_type,
|
|
26
|
+
message: typeof p.error_message === "string" ? p.error_message : undefined,
|
|
27
|
+
details: (p.details ?? null),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const errors = p.errors;
|
|
31
|
+
if (Array.isArray(errors) && errors.length > 0 && typeof errors[0] === "object" && errors[0] !== null) {
|
|
32
|
+
const first = errors[0];
|
|
33
|
+
return {
|
|
34
|
+
code: typeof first.code === "string" ? first.code : undefined,
|
|
35
|
+
message: typeof first.message === "string" ? first.message : undefined,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
13
40
|
// ---------------------------------------------------------------------------
|
|
14
41
|
// Client
|
|
15
42
|
// ---------------------------------------------------------------------------
|
|
@@ -122,15 +149,17 @@ export class XmemoryClient {
|
|
|
122
149
|
throw new XmemoryAPIError(`Invalid JSON from server (${res.status}): ${raw.slice(0, 200)}`, res.status);
|
|
123
150
|
}
|
|
124
151
|
if (!res.ok) {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
const structured = extractStructuredError(payload);
|
|
153
|
+
const msg = structured.message ??
|
|
154
|
+
(typeof payload === "object" && payload !== null && "message" in payload
|
|
155
|
+
? String(payload.message)
|
|
156
|
+
: raw.slice(0, 200));
|
|
157
|
+
throw new XmemoryAPIError(`HTTP ${res.status}: ${msg}`, res.status, structured.code, structured.details);
|
|
129
158
|
}
|
|
130
159
|
const response = payload;
|
|
131
160
|
if (response.errors?.length) {
|
|
132
161
|
const first = response.errors[0];
|
|
133
|
-
throw new XmemoryAPIError(`API error: ${first.message} (${first.code})`, res.status);
|
|
162
|
+
throw new XmemoryAPIError(`API error: ${first.message} (${first.code})`, res.status, first.code);
|
|
134
163
|
}
|
|
135
164
|
return response;
|
|
136
165
|
}
|
|
@@ -202,8 +231,15 @@ export class XmemoryClient {
|
|
|
202
231
|
});
|
|
203
232
|
},
|
|
204
233
|
updateInstanceSchema: async (instanceId, schemaText, schemaType, options) => {
|
|
234
|
+
const body = {
|
|
235
|
+
instance_schema: buildInstanceSchema(schemaText, schemaType),
|
|
236
|
+
};
|
|
237
|
+
if (options?.migrationPlan != null)
|
|
238
|
+
body.migration_plan = options.migrationPlan;
|
|
239
|
+
if (options?.confirmDestructive != null)
|
|
240
|
+
body.confirm_destructive = options.confirmDestructive;
|
|
205
241
|
return this._requestOne("PUT", `/instances/${instanceId}/schema`, {
|
|
206
|
-
body
|
|
242
|
+
body,
|
|
207
243
|
timeoutMs: options?.timeoutMs,
|
|
208
244
|
});
|
|
209
245
|
},
|
|
@@ -219,6 +255,41 @@ export class XmemoryClient {
|
|
|
219
255
|
body.current_yml_schema = options.currentYmlSchema;
|
|
220
256
|
return this._requestOne("POST", `/clusters/${clusterId}/instances/generate_schema`, { body, timeoutMs: options?.timeoutMs });
|
|
221
257
|
},
|
|
258
|
+
enhanceSchema: async (clusterId, schemaDescription, currentYmlSchema, options) => {
|
|
259
|
+
return this._requestOne("POST", `/clusters/${clusterId}/instances/generate_schema`, {
|
|
260
|
+
body: { schema_description: schemaDescription, current_yml_schema: currentYmlSchema },
|
|
261
|
+
timeoutMs: options?.timeoutMs,
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
dryRunMigration: async (instanceId, schemaText, schemaType, options) => {
|
|
265
|
+
const body = {
|
|
266
|
+
instance_schema: buildInstanceSchema(schemaText, schemaType),
|
|
267
|
+
};
|
|
268
|
+
if (options?.migrationPlan != null)
|
|
269
|
+
body.migration_plan = options.migrationPlan;
|
|
270
|
+
if (options?.confirmDestructive != null)
|
|
271
|
+
body.confirm_destructive = options.confirmDestructive;
|
|
272
|
+
return this._requestOne("POST", `/instances/${instanceId}/migrations/dry_run`, {
|
|
273
|
+
body,
|
|
274
|
+
timeoutMs: options?.timeoutMs,
|
|
275
|
+
});
|
|
276
|
+
},
|
|
277
|
+
listMigrations: async (instanceId, options) => {
|
|
278
|
+
const params = {
|
|
279
|
+
limit: String(options?.limit ?? 50),
|
|
280
|
+
include_yaml: String(options?.includeYaml ?? false),
|
|
281
|
+
};
|
|
282
|
+
if (options?.beforeId != null)
|
|
283
|
+
params.before_id = options.beforeId;
|
|
284
|
+
return this._requestOne("GET", `/instances/${instanceId}/migrations`, {
|
|
285
|
+
params,
|
|
286
|
+
timeoutMs: options?.timeoutMs,
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
getMigration: async (instanceId, migrationId, options) => {
|
|
290
|
+
const result = await this._requestOne("GET", `/instances/${instanceId}/migrations/${migrationId}`, { params: { include_yaml: String(options?.includeYaml ?? false) }, timeoutMs: options?.timeoutMs });
|
|
291
|
+
return result.record;
|
|
292
|
+
},
|
|
222
293
|
};
|
|
223
294
|
}
|
|
224
295
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export type { AdminNamespace } from "./client.js";
|
|
|
6
6
|
export { DescribeResult, InstanceHandle } from "./instance.js";
|
|
7
7
|
export { XmemoryAPIError, XmemoryHealthCheckError } from "./types.js";
|
|
8
8
|
export { SchemaType } from "./types.js";
|
|
9
|
-
export type { SchemaTypeValue, ExtractionLogic, ReadMode, WriteQueueStatus } from "./types.js";
|
|
10
|
-
export type { XmemoryClientOptions, RequestOptions, ReadOptions, WriteOptions, ExtractOptions, CreateInstanceOptions, GenerateSchemaOptions, } from "./types.js";
|
|
9
|
+
export type { SchemaTypeValue, ExtractionLogic, ReadMode, WriteQueueStatus, FieldType, OnDelete, CastStrategy, DecisionKind, MigrationSource, } from "./types.js";
|
|
10
|
+
export type { XmemoryClientOptions, RequestOptions, ReadOptions, WriteOptions, ExtractOptions, CreateInstanceOptions, GenerateSchemaOptions, UpdateInstanceSchemaOptions, DryRunMigrationOptions, ListMigrationsOptions, GetMigrationOptions, SuggestionRequestOptions, } from "./types.js";
|
|
11
11
|
export type { ClusterInfo, InstanceInfo, InstanceSchemaInfo, ReadResult, WriteResult, AsyncWriteResult, WriteStatusResult, ExtractResult, GenerateSchemaResult, ToolDescription, ToolParameterDescription, RawDescribeResult, } from "./types.js";
|
|
12
|
+
export type { MigrationPlan, MigrationOp, FieldSpec, AddObject, RemoveObject, RenameObject, ChangeObject, AddField, RemoveField, RenameField, ChangeField, AddRelation, RemoveRelation, RenameRelation, ChangeRelation, DefaultValue, EnumValues, } from "./types.js";
|
|
13
|
+
export type { EnhanceSchemaResult, PlanSummary, DryRunResult, MigrationRecord, ListMigrationsResult, GetMigrationResult, ConsolidatedProposal, ProposalItem, ReviewSuggestionsResult, DecisionInput, RecordedDecision, DependencyWarning, DecideSuggestionsResult, ApplyPendingDecisionsResult, } from "./types.js";
|
package/dist/instance.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* InstanceHandle — scoped data operations on a single xmemory instance.
|
|
3
3
|
*/
|
|
4
|
-
import type { AsyncWriteResult, ExtractOptions, ExtractResult, InstanceSchemaInfo, RawDescribeResult, ReadOptions, ReadResult, RequestOneFn, RequestOptions, ToolDescription, WriteOptions, WriteResult, WriteStatusResult } from "./types.js";
|
|
4
|
+
import type { ApplyPendingDecisionsResult, AsyncWriteResult, DecideSuggestionsResult, DecisionInput, ExtractOptions, ExtractResult, InstanceSchemaInfo, RawDescribeResult, ReadOptions, ReadResult, RequestOneFn, RequestOptions, ReviewSuggestionsResult, SuggestionRequestOptions, ToolDescription, WriteOptions, WriteResult, WriteStatusResult } from "./types.js";
|
|
5
5
|
export declare class DescribeResult {
|
|
6
6
|
readonly instanceId: string;
|
|
7
7
|
readonly instanceName: string;
|
|
@@ -36,6 +36,32 @@ export declare class InstanceHandle {
|
|
|
36
36
|
writeStatus(writeId: string, options?: RequestOptions): Promise<WriteStatusResult>;
|
|
37
37
|
extract(text: string, options?: ExtractOptions): Promise<ExtractResult>;
|
|
38
38
|
getSchema(options?: RequestOptions): Promise<InstanceSchemaInfo>;
|
|
39
|
+
/**
|
|
40
|
+
* Return the current consolidated schema-improvement proposal (step 1).
|
|
41
|
+
*
|
|
42
|
+
* The result's `proposal.proposal_version` is the optimistic-concurrency
|
|
43
|
+
* token you pass to `decideSuggestions` / `applyPendingDecisions`. When
|
|
44
|
+
* `status === "evolution_in_progress"`, back off for `retry_after_seconds`
|
|
45
|
+
* and retry instead of blocking on the in-flight migration.
|
|
46
|
+
*/
|
|
47
|
+
reviewSuggestions(options?: SuggestionRequestOptions): Promise<ReviewSuggestionsResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Record accept/reject/defer decisions for proposal items, in bulk (step 2).
|
|
50
|
+
*
|
|
51
|
+
* `proposalVersion` must be the token from the latest `reviewSuggestions`; a
|
|
52
|
+
* stale token throws `XmemoryAPIError` with `code === "stale_proposal_version"`.
|
|
53
|
+
* The result's `next_proposal_version` can be passed straight to
|
|
54
|
+
* `applyPendingDecisions`.
|
|
55
|
+
*/
|
|
56
|
+
decideSuggestions(proposalVersion: string, decisions: DecisionInput[], options?: SuggestionRequestOptions): Promise<DecideSuggestionsResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Commit accepted decisions as a single migration (step 3).
|
|
59
|
+
*
|
|
60
|
+
* `status === "nothing_to_apply"` means no accepted items were left. A stale
|
|
61
|
+
* `proposalVersion` or an unmet dependency throws `XmemoryAPIError`
|
|
62
|
+
* (`code === "stale_proposal_version"` / `"dependency_closure_failed"`).
|
|
63
|
+
*/
|
|
64
|
+
applyPendingDecisions(proposalVersion: string, options?: SuggestionRequestOptions): Promise<ApplyPendingDecisionsResult>;
|
|
39
65
|
/**
|
|
40
66
|
* Return agent-facing tool descriptions enriched with the instance schema.
|
|
41
67
|
*
|
package/dist/instance.js
CHANGED
|
@@ -161,6 +161,57 @@ export class InstanceHandle {
|
|
|
161
161
|
timeoutMs: options?.timeoutMs,
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
+
// -- Schema evolution (suggestion engine) ---------------------------------
|
|
165
|
+
/**
|
|
166
|
+
* Return the current consolidated schema-improvement proposal (step 1).
|
|
167
|
+
*
|
|
168
|
+
* The result's `proposal.proposal_version` is the optimistic-concurrency
|
|
169
|
+
* token you pass to `decideSuggestions` / `applyPendingDecisions`. When
|
|
170
|
+
* `status === "evolution_in_progress"`, back off for `retry_after_seconds`
|
|
171
|
+
* and retry instead of blocking on the in-flight migration.
|
|
172
|
+
*/
|
|
173
|
+
async reviewSuggestions(options) {
|
|
174
|
+
const body = {};
|
|
175
|
+
if (options?.sessionId != null)
|
|
176
|
+
body.session_id = options.sessionId;
|
|
177
|
+
return this._requestOne("POST", `/instances/${this.id}/suggestions/review`, {
|
|
178
|
+
body,
|
|
179
|
+
timeoutMs: options?.timeoutMs,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Record accept/reject/defer decisions for proposal items, in bulk (step 2).
|
|
184
|
+
*
|
|
185
|
+
* `proposalVersion` must be the token from the latest `reviewSuggestions`; a
|
|
186
|
+
* stale token throws `XmemoryAPIError` with `code === "stale_proposal_version"`.
|
|
187
|
+
* The result's `next_proposal_version` can be passed straight to
|
|
188
|
+
* `applyPendingDecisions`.
|
|
189
|
+
*/
|
|
190
|
+
async decideSuggestions(proposalVersion, decisions, options) {
|
|
191
|
+
const body = { proposal_version: proposalVersion, decisions };
|
|
192
|
+
if (options?.sessionId != null)
|
|
193
|
+
body.session_id = options.sessionId;
|
|
194
|
+
return this._requestOne("POST", `/instances/${this.id}/suggestions/decide`, {
|
|
195
|
+
body,
|
|
196
|
+
timeoutMs: options?.timeoutMs,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Commit accepted decisions as a single migration (step 3).
|
|
201
|
+
*
|
|
202
|
+
* `status === "nothing_to_apply"` means no accepted items were left. A stale
|
|
203
|
+
* `proposalVersion` or an unmet dependency throws `XmemoryAPIError`
|
|
204
|
+
* (`code === "stale_proposal_version"` / `"dependency_closure_failed"`).
|
|
205
|
+
*/
|
|
206
|
+
async applyPendingDecisions(proposalVersion, options) {
|
|
207
|
+
const body = { proposal_version: proposalVersion };
|
|
208
|
+
if (options?.sessionId != null)
|
|
209
|
+
body.session_id = options.sessionId;
|
|
210
|
+
return this._requestOne("POST", `/instances/${this.id}/suggestions/apply`, {
|
|
211
|
+
body,
|
|
212
|
+
timeoutMs: options?.timeoutMs,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
164
215
|
/**
|
|
165
216
|
* Return agent-facing tool descriptions enriched with the instance schema.
|
|
166
217
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -8,10 +8,28 @@ export declare const SchemaType: {
|
|
|
8
8
|
export type SchemaTypeValue = (typeof SchemaType)[keyof typeof SchemaType];
|
|
9
9
|
export type ExtractionLogic = "fast" | "regular" | "deep";
|
|
10
10
|
export type ReadMode = "single-answer" | "raw-tables" | "xresponse";
|
|
11
|
-
export type WriteQueueStatus = "queued" | "processing" | "completed" | "failed" | "not_found";
|
|
11
|
+
export type WriteQueueStatus = "queued" | "processing" | "extracting" | "extracted" | "applying" | "completed" | "failed" | "not_found";
|
|
12
12
|
export declare class XmemoryAPIError extends Error {
|
|
13
13
|
readonly status?: number | undefined;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Structured error code when the server returned one. For the
|
|
16
|
+
* schema-evolution endpoints this is the `error_type` discriminator
|
|
17
|
+
* (e.g. `"stale_proposal_version"`, `"destructive_confirmation_required"`).
|
|
18
|
+
* Pattern match on this instead of parsing the message.
|
|
19
|
+
*/
|
|
20
|
+
readonly code?: string | undefined;
|
|
21
|
+
/** Optional structured `details` payload attached to some errors. */
|
|
22
|
+
readonly details?: (Record<string, unknown> | null) | undefined;
|
|
23
|
+
constructor(message: string, status?: number | undefined,
|
|
24
|
+
/**
|
|
25
|
+
* Structured error code when the server returned one. For the
|
|
26
|
+
* schema-evolution endpoints this is the `error_type` discriminator
|
|
27
|
+
* (e.g. `"stale_proposal_version"`, `"destructive_confirmation_required"`).
|
|
28
|
+
* Pattern match on this instead of parsing the message.
|
|
29
|
+
*/
|
|
30
|
+
code?: string | undefined,
|
|
31
|
+
/** Optional structured `details` payload attached to some errors. */
|
|
32
|
+
details?: (Record<string, unknown> | null) | undefined);
|
|
15
33
|
}
|
|
16
34
|
export declare class XmemoryHealthCheckError extends XmemoryAPIError {
|
|
17
35
|
constructor(message: string, status?: number);
|
|
@@ -28,6 +46,10 @@ export interface InstanceInfo {
|
|
|
28
46
|
readonly name: string;
|
|
29
47
|
readonly description: string | null;
|
|
30
48
|
readonly data_schema: Record<string, unknown> | null;
|
|
49
|
+
readonly migration_id?: string | null;
|
|
50
|
+
readonly prior_version?: number | null;
|
|
51
|
+
readonly new_version?: number | null;
|
|
52
|
+
readonly migration_warnings?: string[] | null;
|
|
31
53
|
}
|
|
32
54
|
export interface InstanceSchemaInfo {
|
|
33
55
|
readonly data_schema: Record<string, unknown>;
|
|
@@ -40,7 +62,6 @@ export interface WriteResult {
|
|
|
40
62
|
readonly write_id: string;
|
|
41
63
|
readonly trace_id: string | null;
|
|
42
64
|
readonly cleaned_objects: unknown;
|
|
43
|
-
readonly diff_plan: unknown;
|
|
44
65
|
}
|
|
45
66
|
export interface AsyncWriteResult {
|
|
46
67
|
readonly write_id: string;
|
|
@@ -58,6 +79,214 @@ export interface ExtractResult {
|
|
|
58
79
|
export interface GenerateSchemaResult {
|
|
59
80
|
readonly data_schema: Record<string, unknown>;
|
|
60
81
|
}
|
|
82
|
+
export type FieldType = "str" | "string" | "int" | "integer" | "float" | "bool" | "boolean";
|
|
83
|
+
export type OnDelete = "nullify" | "cascade";
|
|
84
|
+
export type CastStrategy = "safe_implicit" | "explicit_using" | "lossy";
|
|
85
|
+
export type DecisionKind = "accept" | "reject" | "defer";
|
|
86
|
+
export type MigrationSource = "direct" | "suggestion_engine";
|
|
87
|
+
export type DefaultValue = string | number | boolean | null;
|
|
88
|
+
export type EnumValues = Array<string | number | boolean> | null;
|
|
89
|
+
export interface FieldSpec {
|
|
90
|
+
name: string;
|
|
91
|
+
type: FieldType;
|
|
92
|
+
required?: boolean;
|
|
93
|
+
description?: string | null;
|
|
94
|
+
default?: DefaultValue;
|
|
95
|
+
enum?: EnumValues;
|
|
96
|
+
}
|
|
97
|
+
export interface AddObject {
|
|
98
|
+
op_type: "add_object";
|
|
99
|
+
name: string;
|
|
100
|
+
description?: string | null;
|
|
101
|
+
primary_key: string[];
|
|
102
|
+
fields: FieldSpec[];
|
|
103
|
+
should_backfill?: boolean;
|
|
104
|
+
}
|
|
105
|
+
export interface RemoveObject {
|
|
106
|
+
op_type: "remove_object";
|
|
107
|
+
name: string;
|
|
108
|
+
}
|
|
109
|
+
export interface RenameObject {
|
|
110
|
+
op_type: "rename_object";
|
|
111
|
+
old_name: string;
|
|
112
|
+
new_name: string;
|
|
113
|
+
}
|
|
114
|
+
export interface ChangeObject {
|
|
115
|
+
op_type: "change_object";
|
|
116
|
+
name: string;
|
|
117
|
+
new_primary_key?: string[] | null;
|
|
118
|
+
new_description?: string | null;
|
|
119
|
+
}
|
|
120
|
+
export interface AddField {
|
|
121
|
+
op_type: "add_field";
|
|
122
|
+
object_name: string;
|
|
123
|
+
field_name: string;
|
|
124
|
+
field_type: FieldType;
|
|
125
|
+
required?: boolean;
|
|
126
|
+
description?: string | null;
|
|
127
|
+
default?: DefaultValue;
|
|
128
|
+
enum?: EnumValues;
|
|
129
|
+
should_backfill?: boolean;
|
|
130
|
+
}
|
|
131
|
+
export interface RemoveField {
|
|
132
|
+
op_type: "remove_field";
|
|
133
|
+
object_name: string;
|
|
134
|
+
field_name: string;
|
|
135
|
+
}
|
|
136
|
+
export interface RenameField {
|
|
137
|
+
op_type: "rename_field";
|
|
138
|
+
object_name: string;
|
|
139
|
+
old_name: string;
|
|
140
|
+
new_name: string;
|
|
141
|
+
}
|
|
142
|
+
export interface ChangeField {
|
|
143
|
+
op_type: "change_field";
|
|
144
|
+
object_name: string;
|
|
145
|
+
field_name: string;
|
|
146
|
+
new_type?: FieldType | null;
|
|
147
|
+
new_required?: boolean | null;
|
|
148
|
+
new_description?: string | null;
|
|
149
|
+
new_default?: DefaultValue;
|
|
150
|
+
new_enum?: EnumValues;
|
|
151
|
+
clear_default?: boolean;
|
|
152
|
+
clear_enum?: boolean;
|
|
153
|
+
cast_strategy?: CastStrategy | null;
|
|
154
|
+
using_expression?: string | null;
|
|
155
|
+
confirm_data_loss?: boolean;
|
|
156
|
+
}
|
|
157
|
+
export interface AddRelation {
|
|
158
|
+
op_type: "add_relation";
|
|
159
|
+
name: string;
|
|
160
|
+
description?: string | null;
|
|
161
|
+
objects: Record<string, string>;
|
|
162
|
+
keys?: Record<string, string[]> | null;
|
|
163
|
+
on_delete?: Record<string, OnDelete> | null;
|
|
164
|
+
should_backfill?: boolean;
|
|
165
|
+
}
|
|
166
|
+
export interface RemoveRelation {
|
|
167
|
+
op_type: "remove_relation";
|
|
168
|
+
name: string;
|
|
169
|
+
}
|
|
170
|
+
export interface RenameRelation {
|
|
171
|
+
op_type: "rename_relation";
|
|
172
|
+
old_name: string;
|
|
173
|
+
new_name: string;
|
|
174
|
+
}
|
|
175
|
+
export interface ChangeRelation {
|
|
176
|
+
op_type: "change_relation";
|
|
177
|
+
name: string;
|
|
178
|
+
new_keys?: Record<string, string[]> | null;
|
|
179
|
+
new_on_delete?: Record<string, OnDelete> | null;
|
|
180
|
+
new_description?: string | null;
|
|
181
|
+
}
|
|
182
|
+
export type MigrationOp = AddObject | RemoveObject | RenameObject | ChangeObject | AddField | RemoveField | RenameField | ChangeField | AddRelation | RemoveRelation | RenameRelation | ChangeRelation;
|
|
183
|
+
export interface MigrationPlan {
|
|
184
|
+
ops: MigrationOp[];
|
|
185
|
+
}
|
|
186
|
+
export interface EnhanceSchemaResult {
|
|
187
|
+
readonly data_schema: Record<string, unknown>;
|
|
188
|
+
readonly migration_plan: MigrationPlan | null;
|
|
189
|
+
readonly summary: string | null;
|
|
190
|
+
readonly warnings: Record<string, unknown>[];
|
|
191
|
+
readonly repair_log: Record<string, unknown>[];
|
|
192
|
+
}
|
|
193
|
+
export interface PlanSummary {
|
|
194
|
+
readonly count_by_op_type: Record<string, number>;
|
|
195
|
+
readonly total: number;
|
|
196
|
+
}
|
|
197
|
+
export interface DryRunResult {
|
|
198
|
+
readonly status: "ok";
|
|
199
|
+
readonly instance_id: string;
|
|
200
|
+
readonly current_version: number;
|
|
201
|
+
readonly statements: string[];
|
|
202
|
+
readonly warnings: string[];
|
|
203
|
+
readonly plan_summary: PlanSummary;
|
|
204
|
+
readonly requires_metadata_sync: boolean;
|
|
205
|
+
}
|
|
206
|
+
export interface MigrationRecord {
|
|
207
|
+
readonly id: string;
|
|
208
|
+
readonly applied_at: string;
|
|
209
|
+
readonly source: MigrationSource;
|
|
210
|
+
readonly decided_by: string | null;
|
|
211
|
+
readonly prior_version: number;
|
|
212
|
+
readonly new_version: number;
|
|
213
|
+
readonly ops: Record<string, unknown>[];
|
|
214
|
+
readonly ops_summary: PlanSummary;
|
|
215
|
+
readonly notes: string | null;
|
|
216
|
+
readonly yaml_before: string | null;
|
|
217
|
+
readonly yaml_after: string | null;
|
|
218
|
+
}
|
|
219
|
+
export interface ListMigrationsResult {
|
|
220
|
+
readonly status: "ok";
|
|
221
|
+
readonly instance_id: string;
|
|
222
|
+
readonly items: MigrationRecord[];
|
|
223
|
+
readonly next_before_id: string | null;
|
|
224
|
+
readonly has_more: boolean;
|
|
225
|
+
}
|
|
226
|
+
export interface GetMigrationResult {
|
|
227
|
+
readonly status: "ok";
|
|
228
|
+
readonly instance_id: string;
|
|
229
|
+
readonly record: MigrationRecord;
|
|
230
|
+
}
|
|
231
|
+
export interface ProposalItem {
|
|
232
|
+
readonly item_fingerprint: string;
|
|
233
|
+
/** Raw op dict (forward-compatible). Cast to `MigrationOp` when needed. */
|
|
234
|
+
readonly op: Record<string, unknown>;
|
|
235
|
+
readonly evidence_feedback_ids: string[];
|
|
236
|
+
readonly evidence_query_samples: string[];
|
|
237
|
+
readonly frequency: number;
|
|
238
|
+
readonly depends_on: string[];
|
|
239
|
+
readonly current_decision: string | null;
|
|
240
|
+
readonly rationale: string;
|
|
241
|
+
}
|
|
242
|
+
export interface ConsolidatedProposal {
|
|
243
|
+
readonly instance_id: string;
|
|
244
|
+
readonly proposal_version: string;
|
|
245
|
+
readonly schema_version: number;
|
|
246
|
+
readonly items: ProposalItem[];
|
|
247
|
+
readonly generated_at: string;
|
|
248
|
+
readonly notes: string[];
|
|
249
|
+
}
|
|
250
|
+
export interface ReviewSuggestionsResult {
|
|
251
|
+
readonly status: "ok" | "evolution_in_progress";
|
|
252
|
+
readonly instance_id: string;
|
|
253
|
+
readonly proposal: ConsolidatedProposal | null;
|
|
254
|
+
readonly retry_after_seconds: number | null;
|
|
255
|
+
}
|
|
256
|
+
export interface DecisionInput {
|
|
257
|
+
item_fingerprint: string;
|
|
258
|
+
decision: DecisionKind;
|
|
259
|
+
edits?: Record<string, unknown> | null;
|
|
260
|
+
}
|
|
261
|
+
export interface RecordedDecision {
|
|
262
|
+
readonly item_fingerprint: string;
|
|
263
|
+
readonly decision_id: string;
|
|
264
|
+
}
|
|
265
|
+
export interface DependencyWarning {
|
|
266
|
+
readonly kind: string;
|
|
267
|
+
readonly item_fingerprint: string;
|
|
268
|
+
readonly related_fingerprints: string[];
|
|
269
|
+
readonly related_summaries: string[];
|
|
270
|
+
readonly guidance: string;
|
|
271
|
+
}
|
|
272
|
+
export interface DecideSuggestionsResult {
|
|
273
|
+
readonly status: "ok";
|
|
274
|
+
readonly instance_id: string;
|
|
275
|
+
readonly decisions_recorded: RecordedDecision[];
|
|
276
|
+
readonly warnings: DependencyWarning[];
|
|
277
|
+
readonly next_proposal_version: string;
|
|
278
|
+
}
|
|
279
|
+
export interface ApplyPendingDecisionsResult {
|
|
280
|
+
readonly status: "ok" | "nothing_to_apply";
|
|
281
|
+
readonly instance_id: string;
|
|
282
|
+
readonly migration_id: string | null;
|
|
283
|
+
readonly prior_version: number;
|
|
284
|
+
readonly new_version: number;
|
|
285
|
+
readonly applied_items: string[];
|
|
286
|
+
readonly summary: string;
|
|
287
|
+
readonly warnings: string[];
|
|
288
|
+
readonly notes: string[];
|
|
289
|
+
}
|
|
61
290
|
export interface ToolParameterDescription {
|
|
62
291
|
readonly name: string;
|
|
63
292
|
readonly type: string;
|
|
@@ -113,6 +342,33 @@ export interface GenerateSchemaOptions {
|
|
|
113
342
|
currentYmlSchema?: string;
|
|
114
343
|
timeoutMs?: number;
|
|
115
344
|
}
|
|
345
|
+
export interface UpdateInstanceSchemaOptions {
|
|
346
|
+
/** Serialized migration plan from `enhanceSchema`; required for non-additive changes. */
|
|
347
|
+
migrationPlan?: MigrationPlan | Record<string, unknown>;
|
|
348
|
+
/** Authorise ops that drop data (remove object/field, lossy type cast). */
|
|
349
|
+
confirmDestructive?: boolean;
|
|
350
|
+
timeoutMs?: number;
|
|
351
|
+
}
|
|
352
|
+
export interface DryRunMigrationOptions {
|
|
353
|
+
migrationPlan?: MigrationPlan | Record<string, unknown>;
|
|
354
|
+
confirmDestructive?: boolean;
|
|
355
|
+
timeoutMs?: number;
|
|
356
|
+
}
|
|
357
|
+
export interface ListMigrationsOptions {
|
|
358
|
+
limit?: number;
|
|
359
|
+
beforeId?: string;
|
|
360
|
+
includeYaml?: boolean;
|
|
361
|
+
timeoutMs?: number;
|
|
362
|
+
}
|
|
363
|
+
export interface GetMigrationOptions {
|
|
364
|
+
includeYaml?: boolean;
|
|
365
|
+
timeoutMs?: number;
|
|
366
|
+
}
|
|
367
|
+
export interface SuggestionRequestOptions {
|
|
368
|
+
/** Optional free-form session ID for end-to-end tracing. */
|
|
369
|
+
sessionId?: string;
|
|
370
|
+
timeoutMs?: number;
|
|
371
|
+
}
|
|
116
372
|
export interface ApiError {
|
|
117
373
|
readonly code: string;
|
|
118
374
|
readonly message: string;
|
package/dist/types.js
CHANGED
|
@@ -10,9 +10,22 @@ export const SchemaType = { YML: 0, JSON: 1 };
|
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
11
|
export class XmemoryAPIError extends Error {
|
|
12
12
|
status;
|
|
13
|
-
|
|
13
|
+
code;
|
|
14
|
+
details;
|
|
15
|
+
constructor(message, status,
|
|
16
|
+
/**
|
|
17
|
+
* Structured error code when the server returned one. For the
|
|
18
|
+
* schema-evolution endpoints this is the `error_type` discriminator
|
|
19
|
+
* (e.g. `"stale_proposal_version"`, `"destructive_confirmation_required"`).
|
|
20
|
+
* Pattern match on this instead of parsing the message.
|
|
21
|
+
*/
|
|
22
|
+
code,
|
|
23
|
+
/** Optional structured `details` payload attached to some errors. */
|
|
24
|
+
details) {
|
|
14
25
|
super(message);
|
|
15
26
|
this.status = status;
|
|
27
|
+
this.code = code;
|
|
28
|
+
this.details = details;
|
|
16
29
|
this.name = "XmemoryAPIError";
|
|
17
30
|
}
|
|
18
31
|
}
|