@joshuaswarren/openclaw-engram 9.1.28 → 9.1.29
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/dist/index.js +256 -102
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7549,7 +7549,8 @@ var GraphDashboardServer = class {
|
|
|
7549
7549
|
|
|
7550
7550
|
// src/access-http.ts
|
|
7551
7551
|
import { createServer as createServer3 } from "http";
|
|
7552
|
-
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
7552
|
+
import { randomUUID as randomUUID2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
7553
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
7553
7554
|
import { existsSync } from "fs";
|
|
7554
7555
|
import { readFile as readFile15 } from "fs/promises";
|
|
7555
7556
|
import path22 from "path";
|
|
@@ -8041,6 +8042,151 @@ ${body}`;
|
|
|
8041
8042
|
}
|
|
8042
8043
|
};
|
|
8043
8044
|
|
|
8045
|
+
// src/access-schema.ts
|
|
8046
|
+
import { z as z2 } from "zod";
|
|
8047
|
+
function formatZodError(error) {
|
|
8048
|
+
return {
|
|
8049
|
+
error: "request validation failed",
|
|
8050
|
+
code: "validation_error",
|
|
8051
|
+
details: error.issues.map((issue) => ({
|
|
8052
|
+
field: issue.path.join(".") || "(root)",
|
|
8053
|
+
message: issue.message
|
|
8054
|
+
}))
|
|
8055
|
+
};
|
|
8056
|
+
}
|
|
8057
|
+
var namespaceSchema = z2.string().trim().max(256).optional();
|
|
8058
|
+
var sessionKeySchema = z2.string().trim().min(1).max(512).optional();
|
|
8059
|
+
var idempotencyKeySchema = z2.string().trim().min(1).max(256).optional();
|
|
8060
|
+
var dryRunSchema = z2.boolean().optional();
|
|
8061
|
+
var schemaVersionSchema = z2.number().int().optional();
|
|
8062
|
+
var recallRequestSchema = z2.object({
|
|
8063
|
+
query: z2.string().min(1, "query is required"),
|
|
8064
|
+
sessionKey: sessionKeySchema,
|
|
8065
|
+
namespace: namespaceSchema,
|
|
8066
|
+
topK: z2.number().int().min(0).max(200).optional(),
|
|
8067
|
+
mode: z2.enum(["auto", "no_recall", "minimal", "full", "graph_mode"]).optional(),
|
|
8068
|
+
includeDebug: z2.boolean().optional()
|
|
8069
|
+
});
|
|
8070
|
+
var recallExplainRequestSchema = z2.object({
|
|
8071
|
+
sessionKey: sessionKeySchema,
|
|
8072
|
+
namespace: namespaceSchema
|
|
8073
|
+
});
|
|
8074
|
+
var messageSchema = z2.object({
|
|
8075
|
+
role: z2.enum(["user", "assistant"]),
|
|
8076
|
+
content: z2.string().min(1, "message content must be non-empty")
|
|
8077
|
+
});
|
|
8078
|
+
var observeRequestSchema = z2.object({
|
|
8079
|
+
sessionKey: z2.string().trim().min(1, "sessionKey is required").max(512),
|
|
8080
|
+
messages: z2.array(messageSchema).min(1, "messages must be a non-empty array"),
|
|
8081
|
+
namespace: namespaceSchema,
|
|
8082
|
+
skipExtraction: z2.boolean().optional()
|
|
8083
|
+
});
|
|
8084
|
+
var writeContentSchema = z2.string().min(1, "content is required").max(5e4);
|
|
8085
|
+
var categorySchema = z2.enum([
|
|
8086
|
+
"fact",
|
|
8087
|
+
"preference",
|
|
8088
|
+
"correction",
|
|
8089
|
+
"entity",
|
|
8090
|
+
"decision",
|
|
8091
|
+
"relationship",
|
|
8092
|
+
"principle",
|
|
8093
|
+
"commitment",
|
|
8094
|
+
"moment",
|
|
8095
|
+
"skill",
|
|
8096
|
+
"rule"
|
|
8097
|
+
]).optional();
|
|
8098
|
+
var confidenceSchema = z2.number().min(0).max(1).optional();
|
|
8099
|
+
var tagsSchema = z2.array(z2.string().max(256)).max(50).optional();
|
|
8100
|
+
var entityRefSchema = z2.string().trim().max(512).optional();
|
|
8101
|
+
var ttlSchema = z2.string().trim().max(128).optional();
|
|
8102
|
+
var sourceReasonSchema = z2.string().trim().max(2e3).optional();
|
|
8103
|
+
var memoryStoreRequestSchema = z2.object({
|
|
8104
|
+
schemaVersion: schemaVersionSchema,
|
|
8105
|
+
idempotencyKey: idempotencyKeySchema,
|
|
8106
|
+
dryRun: dryRunSchema,
|
|
8107
|
+
sessionKey: sessionKeySchema,
|
|
8108
|
+
content: writeContentSchema,
|
|
8109
|
+
category: categorySchema,
|
|
8110
|
+
confidence: confidenceSchema,
|
|
8111
|
+
namespace: namespaceSchema,
|
|
8112
|
+
tags: tagsSchema,
|
|
8113
|
+
entityRef: entityRefSchema,
|
|
8114
|
+
ttl: ttlSchema,
|
|
8115
|
+
sourceReason: sourceReasonSchema
|
|
8116
|
+
});
|
|
8117
|
+
var suggestionSubmitRequestSchema = memoryStoreRequestSchema;
|
|
8118
|
+
var reviewDispositionRequestSchema = z2.object({
|
|
8119
|
+
memoryId: z2.string().trim().min(1, "memoryId is required"),
|
|
8120
|
+
status: z2.enum([
|
|
8121
|
+
"active",
|
|
8122
|
+
"pending_review",
|
|
8123
|
+
"quarantined",
|
|
8124
|
+
"rejected",
|
|
8125
|
+
"superseded",
|
|
8126
|
+
"archived"
|
|
8127
|
+
]),
|
|
8128
|
+
reasonCode: z2.string().trim().min(1, "reasonCode is required"),
|
|
8129
|
+
namespace: namespaceSchema
|
|
8130
|
+
});
|
|
8131
|
+
var trustZonePromoteRequestSchema = z2.object({
|
|
8132
|
+
recordId: z2.string().trim().min(1, "recordId is required"),
|
|
8133
|
+
targetZone: z2.enum(["working", "trusted"], {
|
|
8134
|
+
errorMap: () => ({ message: "targetZone must be 'working' or 'trusted'" })
|
|
8135
|
+
}),
|
|
8136
|
+
promotionReason: z2.string().trim().min(1, "promotionReason is required"),
|
|
8137
|
+
recordedAt: z2.string().trim().optional(),
|
|
8138
|
+
summary: z2.string().trim().max(5e3).optional(),
|
|
8139
|
+
dryRun: dryRunSchema,
|
|
8140
|
+
namespace: namespaceSchema
|
|
8141
|
+
});
|
|
8142
|
+
var trustZoneDemoSeedRequestSchema = z2.object({
|
|
8143
|
+
scenario: z2.string().trim().max(256).optional(),
|
|
8144
|
+
recordedAt: z2.string().trim().optional(),
|
|
8145
|
+
dryRun: dryRunSchema,
|
|
8146
|
+
namespace: namespaceSchema
|
|
8147
|
+
});
|
|
8148
|
+
var lcmSearchRequestSchema = z2.object({
|
|
8149
|
+
query: z2.string().min(1, "query is required"),
|
|
8150
|
+
sessionKey: sessionKeySchema,
|
|
8151
|
+
namespace: namespaceSchema,
|
|
8152
|
+
limit: z2.number().int().min(1).max(100).optional()
|
|
8153
|
+
});
|
|
8154
|
+
var daySummaryRequestSchema = z2.object({
|
|
8155
|
+
memories: z2.string().max(1e5).optional(),
|
|
8156
|
+
sessionKey: sessionKeySchema,
|
|
8157
|
+
namespace: namespaceSchema
|
|
8158
|
+
});
|
|
8159
|
+
var schemas = {
|
|
8160
|
+
recall: recallRequestSchema,
|
|
8161
|
+
recallExplain: recallExplainRequestSchema,
|
|
8162
|
+
observe: observeRequestSchema,
|
|
8163
|
+
memoryStore: memoryStoreRequestSchema,
|
|
8164
|
+
suggestionSubmit: suggestionSubmitRequestSchema,
|
|
8165
|
+
reviewDisposition: reviewDispositionRequestSchema,
|
|
8166
|
+
trustZonePromote: trustZonePromoteRequestSchema,
|
|
8167
|
+
trustZoneDemoSeed: trustZoneDemoSeedRequestSchema,
|
|
8168
|
+
lcmSearch: lcmSearchRequestSchema,
|
|
8169
|
+
daySummary: daySummaryRequestSchema
|
|
8170
|
+
};
|
|
8171
|
+
function validateRequest(schemaName, body) {
|
|
8172
|
+
const schema = schemas[schemaName];
|
|
8173
|
+
if (!schema) {
|
|
8174
|
+
return {
|
|
8175
|
+
success: false,
|
|
8176
|
+
error: {
|
|
8177
|
+
error: `unknown schema: ${schemaName}`,
|
|
8178
|
+
code: "validation_error",
|
|
8179
|
+
details: []
|
|
8180
|
+
}
|
|
8181
|
+
};
|
|
8182
|
+
}
|
|
8183
|
+
const result = schema.safeParse(body);
|
|
8184
|
+
if (result.success) {
|
|
8185
|
+
return { success: true, data: result.data };
|
|
8186
|
+
}
|
|
8187
|
+
return { success: false, error: formatZodError(result.error) };
|
|
8188
|
+
}
|
|
8189
|
+
|
|
8044
8190
|
// src/access-http.ts
|
|
8045
8191
|
function resolveDefaultAdminConsolePublicDir() {
|
|
8046
8192
|
const candidates = [
|
|
@@ -8050,15 +8196,20 @@ function resolveDefaultAdminConsolePublicDir() {
|
|
|
8050
8196
|
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
|
8051
8197
|
}
|
|
8052
8198
|
var defaultAdminConsolePublicDir = resolveDefaultAdminConsolePublicDir();
|
|
8199
|
+
var correlationIdStore = new AsyncLocalStorage();
|
|
8053
8200
|
var WRITE_RATE_LIMIT_WINDOW_MS = 6e4;
|
|
8054
8201
|
var WRITE_RATE_LIMIT_MAX_REQUESTS = 30;
|
|
8055
8202
|
var TRUST_ZONE_RECORD_KINDS = ["memory", "artifact", "state", "trajectory", "external"];
|
|
8056
8203
|
var TRUST_ZONE_SOURCE_CLASSES = ["tool_output", "web_content", "subagent_trace", "system_memory", "user_input", "manual"];
|
|
8057
8204
|
var HttpError = class extends Error {
|
|
8058
|
-
constructor(status, message) {
|
|
8205
|
+
constructor(status, message, code, details) {
|
|
8059
8206
|
super(message);
|
|
8060
8207
|
this.status = status;
|
|
8208
|
+
this.code = code ?? `http_${status}`;
|
|
8209
|
+
this.details = details;
|
|
8061
8210
|
}
|
|
8211
|
+
code;
|
|
8212
|
+
details;
|
|
8062
8213
|
};
|
|
8063
8214
|
function hostToUrlAuthority2(host) {
|
|
8064
8215
|
if (host.includes(":") && !host.startsWith("[") && !host.endsWith("]")) {
|
|
@@ -8071,21 +8222,21 @@ function parseTrustZoneKindFilter(raw) {
|
|
|
8071
8222
|
if (TRUST_ZONE_RECORD_KINDS.includes(raw)) {
|
|
8072
8223
|
return raw;
|
|
8073
8224
|
}
|
|
8074
|
-
throw new HttpError(400, `kind must be one of ${TRUST_ZONE_RECORD_KINDS.join("|")}
|
|
8225
|
+
throw new HttpError(400, `kind must be one of ${TRUST_ZONE_RECORD_KINDS.join("|")}`, "invalid_kind_filter");
|
|
8075
8226
|
}
|
|
8076
8227
|
function parseTrustZoneSourceClassFilter(raw) {
|
|
8077
8228
|
if (raw === null) return void 0;
|
|
8078
8229
|
if (TRUST_ZONE_SOURCE_CLASSES.includes(raw)) {
|
|
8079
8230
|
return raw;
|
|
8080
8231
|
}
|
|
8081
|
-
throw new HttpError(400, `sourceClass must be one of ${TRUST_ZONE_SOURCE_CLASSES.join("|")}
|
|
8232
|
+
throw new HttpError(400, `sourceClass must be one of ${TRUST_ZONE_SOURCE_CLASSES.join("|")}`, "invalid_source_class_filter");
|
|
8082
8233
|
}
|
|
8083
8234
|
function parseTrustZoneFilter(raw) {
|
|
8084
8235
|
if (raw === null) return void 0;
|
|
8085
8236
|
if (isTrustZoneName(raw)) {
|
|
8086
8237
|
return raw;
|
|
8087
8238
|
}
|
|
8088
|
-
throw new HttpError(400, "zone must be one of quarantine|working|trusted");
|
|
8239
|
+
throw new HttpError(400, "zone must be one of quarantine|working|trusted", "invalid_zone_filter");
|
|
8089
8240
|
}
|
|
8090
8241
|
var EngramAccessHttpServer = class {
|
|
8091
8242
|
service;
|
|
@@ -8119,21 +8270,26 @@ var EngramAccessHttpServer = class {
|
|
|
8119
8270
|
}
|
|
8120
8271
|
if (this.server) return this.status();
|
|
8121
8272
|
const server = createServer3((req, res) => {
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8273
|
+
const correlationId = randomUUID2();
|
|
8274
|
+
correlationIdStore.run(correlationId, () => {
|
|
8275
|
+
void this.handle(req, res, correlationId).catch((err) => {
|
|
8276
|
+
log.debug(`engram access HTTP request failed [${correlationId}]: ${err}`);
|
|
8277
|
+
if (err instanceof HttpError) {
|
|
8278
|
+
const payload = { error: err.message, code: err.code };
|
|
8279
|
+
if (err.details) payload.details = err.details;
|
|
8280
|
+
this.respondJson(res, err.status, payload);
|
|
8281
|
+
return;
|
|
8282
|
+
}
|
|
8283
|
+
if (err instanceof EngramAccessInputError) {
|
|
8284
|
+
this.respondJson(res, 400, { error: err.message, code: "input_error" });
|
|
8285
|
+
return;
|
|
8286
|
+
}
|
|
8287
|
+
if (res.headersSent) {
|
|
8288
|
+
res.destroy(err);
|
|
8289
|
+
return;
|
|
8290
|
+
}
|
|
8291
|
+
this.respondJson(res, 500, { error: "internal_error", code: "internal_error" });
|
|
8292
|
+
});
|
|
8137
8293
|
});
|
|
8138
8294
|
});
|
|
8139
8295
|
try {
|
|
@@ -8189,18 +8345,20 @@ var EngramAccessHttpServer = class {
|
|
|
8189
8345
|
}
|
|
8190
8346
|
return this.authenticatedPrincipal;
|
|
8191
8347
|
}
|
|
8192
|
-
async handle(req, res) {
|
|
8348
|
+
async handle(req, res, correlationId) {
|
|
8193
8349
|
const parsed = new URL3(req.url ?? "/", `http://${hostToUrlAuthority2(this.host)}`);
|
|
8194
8350
|
const pathname = parsed.pathname;
|
|
8195
8351
|
if (this.adminConsoleEnabled && await this.handleAdminConsole(req, res, pathname)) {
|
|
8196
8352
|
return;
|
|
8197
8353
|
}
|
|
8198
8354
|
if (!this.isAuthorized(req)) {
|
|
8355
|
+
const body = JSON.stringify({ error: "unauthorized", code: "unauthorized" });
|
|
8199
8356
|
res.writeHead(401, {
|
|
8200
8357
|
"content-type": "application/json; charset=utf-8",
|
|
8201
|
-
"www-authenticate": "Bearer"
|
|
8358
|
+
"www-authenticate": "Bearer",
|
|
8359
|
+
"x-request-id": correlationId
|
|
8202
8360
|
});
|
|
8203
|
-
res.end(
|
|
8361
|
+
res.end(body);
|
|
8204
8362
|
return;
|
|
8205
8363
|
}
|
|
8206
8364
|
if (req.method === "POST" && pathname === "/mcp") {
|
|
@@ -8212,34 +8370,34 @@ var EngramAccessHttpServer = class {
|
|
|
8212
8370
|
return;
|
|
8213
8371
|
}
|
|
8214
8372
|
if (req.method === "POST" && pathname === "/engram/v1/recall") {
|
|
8215
|
-
const body = await this.
|
|
8373
|
+
const body = await this.readValidatedBody(req, "recall");
|
|
8216
8374
|
const response = await this.service.recall({
|
|
8217
|
-
query:
|
|
8218
|
-
sessionKey:
|
|
8219
|
-
namespace:
|
|
8220
|
-
topK:
|
|
8221
|
-
mode:
|
|
8375
|
+
query: body.query ?? "",
|
|
8376
|
+
sessionKey: body.sessionKey,
|
|
8377
|
+
namespace: body.namespace,
|
|
8378
|
+
topK: body.topK,
|
|
8379
|
+
mode: body.mode,
|
|
8222
8380
|
includeDebug: body.includeDebug === true
|
|
8223
8381
|
});
|
|
8224
8382
|
this.respondJson(res, 200, response);
|
|
8225
8383
|
return;
|
|
8226
8384
|
}
|
|
8227
8385
|
if (req.method === "POST" && pathname === "/engram/v1/recall/explain") {
|
|
8228
|
-
const body = await this.
|
|
8386
|
+
const body = await this.readValidatedBody(req, "recallExplain");
|
|
8229
8387
|
const response = await this.service.recallExplain({
|
|
8230
|
-
sessionKey:
|
|
8231
|
-
namespace:
|
|
8388
|
+
sessionKey: body.sessionKey,
|
|
8389
|
+
namespace: body.namespace
|
|
8232
8390
|
});
|
|
8233
8391
|
this.respondJson(res, 200, response);
|
|
8234
8392
|
return;
|
|
8235
8393
|
}
|
|
8236
8394
|
if (req.method === "POST" && pathname === "/engram/v1/observe") {
|
|
8237
|
-
const body = await this.
|
|
8395
|
+
const body = await this.readValidatedBody(req, "observe");
|
|
8238
8396
|
this.ensureWriteRateLimitAvailable();
|
|
8239
8397
|
const response = await this.service.observe({
|
|
8240
|
-
sessionKey:
|
|
8241
|
-
messages:
|
|
8242
|
-
namespace:
|
|
8398
|
+
sessionKey: body.sessionKey,
|
|
8399
|
+
messages: body.messages,
|
|
8400
|
+
namespace: body.namespace,
|
|
8243
8401
|
authenticatedPrincipal: this.resolveRequestPrincipal(req),
|
|
8244
8402
|
skipExtraction: body.skipExtraction === true
|
|
8245
8403
|
});
|
|
@@ -8248,13 +8406,13 @@ var EngramAccessHttpServer = class {
|
|
|
8248
8406
|
return;
|
|
8249
8407
|
}
|
|
8250
8408
|
if (req.method === "POST" && pathname === "/engram/v1/lcm/search") {
|
|
8251
|
-
const body = await this.
|
|
8409
|
+
const body = await this.readValidatedBody(req, "lcmSearch");
|
|
8252
8410
|
const response = await this.service.lcmSearch({
|
|
8253
|
-
query:
|
|
8254
|
-
sessionKey:
|
|
8255
|
-
namespace:
|
|
8411
|
+
query: body.query,
|
|
8412
|
+
sessionKey: body.sessionKey,
|
|
8413
|
+
namespace: body.namespace,
|
|
8256
8414
|
authenticatedPrincipal: this.resolveRequestPrincipal(req),
|
|
8257
|
-
limit:
|
|
8415
|
+
limit: body.limit
|
|
8258
8416
|
});
|
|
8259
8417
|
this.respondJson(res, 200, response);
|
|
8260
8418
|
return;
|
|
@@ -8264,21 +8422,21 @@ var EngramAccessHttpServer = class {
|
|
|
8264
8422
|
return;
|
|
8265
8423
|
}
|
|
8266
8424
|
if (req.method === "POST" && pathname === "/engram/v1/memories") {
|
|
8267
|
-
const body = await this.
|
|
8425
|
+
const body = await this.readValidatedBody(req, "memoryStore");
|
|
8268
8426
|
const request = {
|
|
8269
|
-
schemaVersion:
|
|
8270
|
-
idempotencyKey:
|
|
8427
|
+
schemaVersion: body.schemaVersion,
|
|
8428
|
+
idempotencyKey: body.idempotencyKey,
|
|
8271
8429
|
dryRun: body.dryRun === true,
|
|
8272
|
-
sessionKey:
|
|
8430
|
+
sessionKey: body.sessionKey,
|
|
8273
8431
|
authenticatedPrincipal: this.resolveRequestPrincipal(req),
|
|
8274
|
-
content:
|
|
8275
|
-
category:
|
|
8276
|
-
confidence:
|
|
8277
|
-
namespace:
|
|
8278
|
-
tags:
|
|
8279
|
-
entityRef:
|
|
8280
|
-
ttl:
|
|
8281
|
-
sourceReason:
|
|
8432
|
+
content: body.content,
|
|
8433
|
+
category: body.category,
|
|
8434
|
+
confidence: body.confidence,
|
|
8435
|
+
namespace: body.namespace,
|
|
8436
|
+
tags: body.tags,
|
|
8437
|
+
entityRef: body.entityRef,
|
|
8438
|
+
ttl: body.ttl,
|
|
8439
|
+
sourceReason: body.sourceReason
|
|
8282
8440
|
};
|
|
8283
8441
|
const idempotencyStatus = await this.service.peekMemoryStoreIdempotency(request);
|
|
8284
8442
|
if (idempotencyStatus === "miss" && request.dryRun !== true) {
|
|
@@ -8292,21 +8450,21 @@ var EngramAccessHttpServer = class {
|
|
|
8292
8450
|
return;
|
|
8293
8451
|
}
|
|
8294
8452
|
if (req.method === "POST" && pathname === "/engram/v1/suggestions") {
|
|
8295
|
-
const body = await this.
|
|
8453
|
+
const body = await this.readValidatedBody(req, "suggestionSubmit");
|
|
8296
8454
|
const request = {
|
|
8297
|
-
schemaVersion:
|
|
8298
|
-
idempotencyKey:
|
|
8455
|
+
schemaVersion: body.schemaVersion,
|
|
8456
|
+
idempotencyKey: body.idempotencyKey,
|
|
8299
8457
|
dryRun: body.dryRun === true,
|
|
8300
|
-
sessionKey:
|
|
8458
|
+
sessionKey: body.sessionKey,
|
|
8301
8459
|
authenticatedPrincipal: this.resolveRequestPrincipal(req),
|
|
8302
|
-
content:
|
|
8303
|
-
category:
|
|
8304
|
-
confidence:
|
|
8305
|
-
namespace:
|
|
8306
|
-
tags:
|
|
8307
|
-
entityRef:
|
|
8308
|
-
ttl:
|
|
8309
|
-
sourceReason:
|
|
8460
|
+
content: body.content,
|
|
8461
|
+
category: body.category,
|
|
8462
|
+
confidence: body.confidence,
|
|
8463
|
+
namespace: body.namespace,
|
|
8464
|
+
tags: body.tags,
|
|
8465
|
+
entityRef: body.entityRef,
|
|
8466
|
+
ttl: body.ttl,
|
|
8467
|
+
sourceReason: body.sourceReason
|
|
8310
8468
|
};
|
|
8311
8469
|
const idempotencyStatus = await this.service.peekSuggestionSubmitIdempotency(request);
|
|
8312
8470
|
if (idempotencyStatus === "miss" && request.dryRun !== true) {
|
|
@@ -8415,17 +8573,13 @@ var EngramAccessHttpServer = class {
|
|
|
8415
8573
|
return;
|
|
8416
8574
|
}
|
|
8417
8575
|
if (req.method === "POST" && pathname === "/engram/v1/review-disposition") {
|
|
8418
|
-
const body = await this.
|
|
8419
|
-
const status = typeof body.status === "string" ? body.status : "";
|
|
8420
|
-
if (status !== "active" && status !== "pending_review" && status !== "quarantined" && status !== "rejected" && status !== "superseded" && status !== "archived") {
|
|
8421
|
-
throw new HttpError(400, "invalid_review_status");
|
|
8422
|
-
}
|
|
8576
|
+
const body = await this.readValidatedBody(req, "reviewDisposition");
|
|
8423
8577
|
this.ensureWriteRateLimitAvailable();
|
|
8424
8578
|
const response = await this.service.reviewDisposition({
|
|
8425
|
-
memoryId:
|
|
8426
|
-
status,
|
|
8427
|
-
reasonCode:
|
|
8428
|
-
namespace:
|
|
8579
|
+
memoryId: body.memoryId,
|
|
8580
|
+
status: body.status,
|
|
8581
|
+
reasonCode: body.reasonCode,
|
|
8582
|
+
namespace: body.namespace,
|
|
8429
8583
|
authenticatedPrincipal: this.resolveRequestPrincipal(req)
|
|
8430
8584
|
});
|
|
8431
8585
|
if (this.shouldCountWriteRateLimit(response)) {
|
|
@@ -8435,31 +8589,19 @@ var EngramAccessHttpServer = class {
|
|
|
8435
8589
|
return;
|
|
8436
8590
|
}
|
|
8437
8591
|
if (req.method === "POST" && pathname === "/engram/v1/trust-zones/promote") {
|
|
8438
|
-
const body = await this.
|
|
8439
|
-
const recordId = typeof body.recordId === "string" ? body.recordId.trim() : "";
|
|
8440
|
-
const targetZone = typeof body.targetZone === "string" ? body.targetZone.trim() : "";
|
|
8441
|
-
const promotionReason = typeof body.promotionReason === "string" ? body.promotionReason.trim() : "";
|
|
8592
|
+
const body = await this.readValidatedBody(req, "trustZonePromote");
|
|
8442
8593
|
const dryRun = body.dryRun === true;
|
|
8443
|
-
if (!recordId) {
|
|
8444
|
-
throw new HttpError(400, "recordId is required");
|
|
8445
|
-
}
|
|
8446
|
-
if (!isTrustZoneName(targetZone)) {
|
|
8447
|
-
throw new HttpError(400, "invalid_trust_zone_target");
|
|
8448
|
-
}
|
|
8449
|
-
if (!promotionReason) {
|
|
8450
|
-
throw new HttpError(400, "promotionReason is required");
|
|
8451
|
-
}
|
|
8452
8594
|
if (!dryRun) {
|
|
8453
8595
|
this.ensureWriteRateLimitAvailable();
|
|
8454
8596
|
}
|
|
8455
8597
|
const response = await this.service.trustZonePromote({
|
|
8456
|
-
recordId,
|
|
8457
|
-
targetZone,
|
|
8458
|
-
promotionReason,
|
|
8459
|
-
recordedAt:
|
|
8460
|
-
summary:
|
|
8598
|
+
recordId: body.recordId,
|
|
8599
|
+
targetZone: body.targetZone,
|
|
8600
|
+
promotionReason: body.promotionReason,
|
|
8601
|
+
recordedAt: body.recordedAt,
|
|
8602
|
+
summary: body.summary,
|
|
8461
8603
|
dryRun,
|
|
8462
|
-
namespace:
|
|
8604
|
+
namespace: body.namespace,
|
|
8463
8605
|
authenticatedPrincipal: this.resolveRequestPrincipal(req)
|
|
8464
8606
|
});
|
|
8465
8607
|
if (this.shouldCountWriteRateLimit(response)) {
|
|
@@ -8469,16 +8611,16 @@ var EngramAccessHttpServer = class {
|
|
|
8469
8611
|
return;
|
|
8470
8612
|
}
|
|
8471
8613
|
if (req.method === "POST" && pathname === "/engram/v1/trust-zones/demo-seed") {
|
|
8472
|
-
const body = await this.
|
|
8614
|
+
const body = await this.readValidatedBody(req, "trustZoneDemoSeed");
|
|
8473
8615
|
const dryRun = body.dryRun === true;
|
|
8474
8616
|
if (!dryRun) {
|
|
8475
8617
|
this.ensureWriteRateLimitAvailable();
|
|
8476
8618
|
}
|
|
8477
8619
|
const response = await this.service.trustZoneDemoSeed({
|
|
8478
|
-
scenario:
|
|
8479
|
-
recordedAt:
|
|
8620
|
+
scenario: body.scenario,
|
|
8621
|
+
recordedAt: body.recordedAt,
|
|
8480
8622
|
dryRun,
|
|
8481
|
-
namespace:
|
|
8623
|
+
namespace: body.namespace,
|
|
8482
8624
|
authenticatedPrincipal: this.resolveRequestPrincipal(req)
|
|
8483
8625
|
});
|
|
8484
8626
|
if (this.shouldCountWriteRateLimit(response)) {
|
|
@@ -8487,7 +8629,7 @@ var EngramAccessHttpServer = class {
|
|
|
8487
8629
|
this.respondJson(res, response.dryRun ? 200 : 201, response);
|
|
8488
8630
|
return;
|
|
8489
8631
|
}
|
|
8490
|
-
this.respondJson(res, 404, { error: "not_found" });
|
|
8632
|
+
this.respondJson(res, 404, { error: "not_found", code: "not_found" });
|
|
8491
8633
|
}
|
|
8492
8634
|
async handleMcpRequest(req, res) {
|
|
8493
8635
|
const body = await this.readJsonBody(req);
|
|
@@ -8519,6 +8661,10 @@ var EngramAccessHttpServer = class {
|
|
|
8519
8661
|
res.statusCode = status;
|
|
8520
8662
|
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
8521
8663
|
res.setHeader("content-length", String(Buffer.byteLength(body)));
|
|
8664
|
+
const cid = correlationIdStore.getStore();
|
|
8665
|
+
if (cid) {
|
|
8666
|
+
res.setHeader("x-request-id", cid);
|
|
8667
|
+
}
|
|
8522
8668
|
res.end(body);
|
|
8523
8669
|
}
|
|
8524
8670
|
async handleAdminConsole(req, res, pathname) {
|
|
@@ -8551,7 +8697,7 @@ var EngramAccessHttpServer = class {
|
|
|
8551
8697
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
8552
8698
|
total += buffer.length;
|
|
8553
8699
|
if (total > this.maxBodyBytes) {
|
|
8554
|
-
throw new HttpError(413, "request_body_too_large");
|
|
8700
|
+
throw new HttpError(413, "request_body_too_large", "request_body_too_large");
|
|
8555
8701
|
}
|
|
8556
8702
|
chunks.push(buffer);
|
|
8557
8703
|
}
|
|
@@ -8562,13 +8708,21 @@ var EngramAccessHttpServer = class {
|
|
|
8562
8708
|
try {
|
|
8563
8709
|
parsed = JSON.parse(raw);
|
|
8564
8710
|
} catch {
|
|
8565
|
-
throw new HttpError(400, "invalid_json");
|
|
8711
|
+
throw new HttpError(400, "invalid_json", "invalid_json");
|
|
8566
8712
|
}
|
|
8567
8713
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8568
|
-
throw new HttpError(400, "invalid_json_object");
|
|
8714
|
+
throw new HttpError(400, "invalid_json_object", "invalid_json_object");
|
|
8569
8715
|
}
|
|
8570
8716
|
return parsed;
|
|
8571
8717
|
}
|
|
8718
|
+
async readValidatedBody(req, schemaName) {
|
|
8719
|
+
const raw = await this.readJsonBody(req);
|
|
8720
|
+
const result = validateRequest(schemaName, raw);
|
|
8721
|
+
if (!result.success) {
|
|
8722
|
+
throw new HttpError(400, result.error.error, "validation_error", result.error.details);
|
|
8723
|
+
}
|
|
8724
|
+
return result.data;
|
|
8725
|
+
}
|
|
8572
8726
|
isAuthorized(req) {
|
|
8573
8727
|
if (!this.authToken) return false;
|
|
8574
8728
|
const raw = req.headers.authorization;
|
|
@@ -8605,7 +8759,7 @@ var EngramAccessHttpServer = class {
|
|
|
8605
8759
|
this.writeRequestTimestamps.shift();
|
|
8606
8760
|
}
|
|
8607
8761
|
if (this.writeRequestTimestamps.length >= WRITE_RATE_LIMIT_MAX_REQUESTS) {
|
|
8608
|
-
throw new HttpError(429, "write_rate_limited");
|
|
8762
|
+
throw new HttpError(429, "write_rate_limited", "write_rate_limited");
|
|
8609
8763
|
}
|
|
8610
8764
|
}
|
|
8611
8765
|
recordWriteRateLimitHit() {
|