stratagate-dsh 0.2.38 → 0.2.39
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/CHANGELOG.md +10 -1
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/client.js +2 -2
- package/dist/index.js +723 -56
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/DSH.md +1 -1
- package/docs/DSH.zh-CN.md +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1263,7 +1263,7 @@ function normalizeRetrievalAssessment(input, batchEvidenceRefs) {
|
|
|
1263
1263
|
}
|
|
1264
1264
|
|
|
1265
1265
|
// packages/core/src/storage.ts
|
|
1266
|
-
var STRATAGATE_STORAGE_SCHEMA_VERSION =
|
|
1266
|
+
var STRATAGATE_STORAGE_SCHEMA_VERSION = 10;
|
|
1267
1267
|
var KNOWLEDGE_GRAPH_PROJECTOR_VERSION = 1;
|
|
1268
1268
|
var StorageConflictError = class extends Error {
|
|
1269
1269
|
constructor(namespace, expectedRevision, actualRevision) {
|
|
@@ -1387,7 +1387,15 @@ function normalizeSnapshot(value) {
|
|
|
1387
1387
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1388
1388
|
blocks: readyLegacyBlocks(legacy.blocks),
|
|
1389
1389
|
summaryJobs: [],
|
|
1390
|
-
extractionJobs: legacyExtractionJobs(legacy.extractionJobs)
|
|
1390
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1391
|
+
externalMemoryImportJobs: []
|
|
1392
|
+
};
|
|
1393
|
+
} else if (schemaVersion === 9) {
|
|
1394
|
+
const legacy = value;
|
|
1395
|
+
snapshot = {
|
|
1396
|
+
...structuredClone(legacy),
|
|
1397
|
+
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1398
|
+
externalMemoryImportJobs: []
|
|
1391
1399
|
};
|
|
1392
1400
|
} else if (schemaVersion === STRATAGATE_STORAGE_SCHEMA_VERSION) {
|
|
1393
1401
|
snapshot = structuredClone(value);
|
|
@@ -1403,7 +1411,8 @@ function normalizeSnapshot(value) {
|
|
|
1403
1411
|
if (!Number.isFinite(snapshot.blockDecayLambda) || snapshot.blockDecayLambda < 0) {
|
|
1404
1412
|
throw new TypeError("Invalid StrataGate snapshot: blockDecayLambda must be a non-negative finite number");
|
|
1405
1413
|
}
|
|
1406
|
-
|
|
1414
|
+
if (!Array.isArray(snapshot.externalMemoryImportJobs)) snapshot.externalMemoryImportJobs = [];
|
|
1415
|
+
for (const key of ["openTail", "blocks", "summaryJobs", "events", "graphNodes", "graphEdges", "graphProjectionJobs", "elements", "extractionJobs", "elementProjectionJobs", "usageReceipts", "ingestionReceipts", "externalMemoryImportJobs"]) {
|
|
1407
1416
|
if (!Array.isArray(snapshot[key])) throw new TypeError(`Invalid StrataGate snapshot: ${key} must be an array`);
|
|
1408
1417
|
}
|
|
1409
1418
|
if (!Array.isArray(snapshot.successfulModelResponses)) snapshot.successfulModelResponses = [];
|
|
@@ -1817,6 +1826,16 @@ CREATE TABLE IF NOT EXISTS ingestion_receipts (
|
|
|
1817
1826
|
PRIMARY KEY (namespace, receipt_id),
|
|
1818
1827
|
FOREIGN KEY (namespace) REFERENCES memory_spaces(namespace) ON DELETE CASCADE
|
|
1819
1828
|
) STRICT;
|
|
1829
|
+
|
|
1830
|
+
CREATE TABLE IF NOT EXISTS external_memory_import_jobs (
|
|
1831
|
+
namespace TEXT NOT NULL,
|
|
1832
|
+
id TEXT NOT NULL,
|
|
1833
|
+
payload_json TEXT NOT NULL,
|
|
1834
|
+
created_at TEXT NOT NULL,
|
|
1835
|
+
updated_at TEXT NOT NULL,
|
|
1836
|
+
PRIMARY KEY (namespace, id),
|
|
1837
|
+
FOREIGN KEY (namespace) REFERENCES memory_spaces(namespace) ON DELETE CASCADE
|
|
1838
|
+
) STRICT;
|
|
1820
1839
|
`;
|
|
1821
1840
|
var THREAD_INDEXES = `
|
|
1822
1841
|
CREATE INDEX IF NOT EXISTS messages_thread_idx ON messages(namespace, thread_id, position);
|
|
@@ -2092,6 +2111,10 @@ var SqliteStorage = class {
|
|
|
2092
2111
|
id: row.receipt_id,
|
|
2093
2112
|
createdAt: row.created_at
|
|
2094
2113
|
}));
|
|
2114
|
+
const externalMemoryImportJobs = this.database.prepare(`
|
|
2115
|
+
SELECT id, payload_json FROM external_memory_import_jobs
|
|
2116
|
+
WHERE namespace = ? ORDER BY created_at, id
|
|
2117
|
+
`).all(key).map((row) => parseJson(row.payload_json, "external_memory_import_jobs.payload_json"));
|
|
2095
2118
|
const graphState = this.database.prepare(`
|
|
2096
2119
|
SELECT nodes_json, edges_json, jobs_json FROM graph_state WHERE namespace = ?
|
|
2097
2120
|
`).get(key);
|
|
@@ -2115,6 +2138,7 @@ var SqliteStorage = class {
|
|
|
2115
2138
|
elementProjectionJobs,
|
|
2116
2139
|
usageReceipts,
|
|
2117
2140
|
ingestionReceipts,
|
|
2141
|
+
externalMemoryImportJobs,
|
|
2118
2142
|
successfulModelResponses
|
|
2119
2143
|
};
|
|
2120
2144
|
assertValidSnapshot(snapshot);
|
|
@@ -2498,6 +2522,14 @@ var SqliteStorage = class {
|
|
|
2498
2522
|
for (const receipt of snapshot.ingestionReceipts) {
|
|
2499
2523
|
insertIngestionReceipt.run(namespace, receipt.id, receipt.createdAt);
|
|
2500
2524
|
}
|
|
2525
|
+
this.database.prepare("DELETE FROM external_memory_import_jobs WHERE namespace = ?").run(namespace);
|
|
2526
|
+
const insertExternalMemoryImportJob = this.database.prepare(`
|
|
2527
|
+
INSERT INTO external_memory_import_jobs (namespace, id, payload_json, created_at, updated_at)
|
|
2528
|
+
VALUES (?, ?, ?, ?, ?)
|
|
2529
|
+
`);
|
|
2530
|
+
for (const job of snapshot.externalMemoryImportJobs) {
|
|
2531
|
+
insertExternalMemoryImportJob.run(namespace, job.id, JSON.stringify(job), job.createdAt, job.updatedAt);
|
|
2532
|
+
}
|
|
2501
2533
|
this.database.prepare("DELETE FROM model_response_history WHERE namespace = ?").run(namespace);
|
|
2502
2534
|
const insertSuccessfulModelResponse = this.database.prepare(`
|
|
2503
2535
|
INSERT INTO model_response_history (namespace, id, kind, response, created_at)
|
|
@@ -2646,6 +2678,10 @@ function errorMessage(error) {
|
|
|
2646
2678
|
return error instanceof Error ? error.message : String(error);
|
|
2647
2679
|
}
|
|
2648
2680
|
var STRATAGATE_CONSTRUCTOR_TOKEN = /* @__PURE__ */ Symbol("StrataGate constructor");
|
|
2681
|
+
var EXTERNAL_MEMORY_AUTO_APPLY_CONFIDENCE = 0.85;
|
|
2682
|
+
function externalMemoryFingerprint(value) {
|
|
2683
|
+
return `${normalizeSearchText(value.title)}\0${normalizeSearchText(value.summary)}`;
|
|
2684
|
+
}
|
|
2649
2685
|
var DERIVATION_MAX_ATTEMPTS = 3;
|
|
2650
2686
|
var DERIVATION_BACKOFF_MS = 1e3;
|
|
2651
2687
|
var StrataGate = class _StrataGate {
|
|
@@ -2673,6 +2709,7 @@ var StrataGate = class _StrataGate {
|
|
|
2673
2709
|
usageReceipts = /* @__PURE__ */ new Map();
|
|
2674
2710
|
successfulModelResponses = [];
|
|
2675
2711
|
ingestionReceipts = /* @__PURE__ */ new Map();
|
|
2712
|
+
externalMemoryImportJobs = /* @__PURE__ */ new Map();
|
|
2676
2713
|
currentTurn = 0;
|
|
2677
2714
|
storage;
|
|
2678
2715
|
namespace;
|
|
@@ -2846,6 +2883,25 @@ var StrataGate = class _StrataGate {
|
|
|
2846
2883
|
get storageRevision() {
|
|
2847
2884
|
return this.revision;
|
|
2848
2885
|
}
|
|
2886
|
+
/** Replace an out-of-date in-memory view with the latest durable namespace snapshot. */
|
|
2887
|
+
async reloadFromStorage() {
|
|
2888
|
+
if (!this.storage || !this.namespace) return false;
|
|
2889
|
+
const previous = this.mutationQueue;
|
|
2890
|
+
let release;
|
|
2891
|
+
this.mutationQueue = new Promise((resolve2) => {
|
|
2892
|
+
release = resolve2;
|
|
2893
|
+
});
|
|
2894
|
+
await previous;
|
|
2895
|
+
try {
|
|
2896
|
+
const loaded = await this.storage.load(this.namespace);
|
|
2897
|
+
if (!loaded || loaded.revision === this.revision) return false;
|
|
2898
|
+
this.restoreSnapshot(loaded.snapshot);
|
|
2899
|
+
this.revision = loaded.revision;
|
|
2900
|
+
return true;
|
|
2901
|
+
} finally {
|
|
2902
|
+
release();
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2849
2905
|
get blockTurnSize() {
|
|
2850
2906
|
return this.blockTurnSizeValue;
|
|
2851
2907
|
}
|
|
@@ -2907,6 +2963,9 @@ var StrataGate = class _StrataGate {
|
|
|
2907
2963
|
listSuccessfulModelResponses() {
|
|
2908
2964
|
return this.successfulModelResponses;
|
|
2909
2965
|
}
|
|
2966
|
+
listExternalMemoryImportJobs() {
|
|
2967
|
+
return [...this.externalMemoryImportJobs.values()].map((job) => structuredClone(job));
|
|
2968
|
+
}
|
|
2910
2969
|
async recordSuccessfulModelResponses(responses) {
|
|
2911
2970
|
if (responses.length === 0) return;
|
|
2912
2971
|
await this.commitMutation(() => {
|
|
@@ -2937,6 +2996,7 @@ var StrataGate = class _StrataGate {
|
|
|
2937
2996
|
elementProjectionJobs: [...this.elementProjectionJobs.values()],
|
|
2938
2997
|
usageReceipts: [...this.usageReceipts.values()],
|
|
2939
2998
|
ingestionReceipts: [...this.ingestionReceipts.values()],
|
|
2999
|
+
externalMemoryImportJobs: [...this.externalMemoryImportJobs.values()],
|
|
2940
3000
|
successfulModelResponses: this.successfulModelResponses
|
|
2941
3001
|
});
|
|
2942
3002
|
}
|
|
@@ -3044,7 +3104,41 @@ var StrataGate = class _StrataGate {
|
|
|
3044
3104
|
* overwritten: MERGE and SUPERSEDE create a new canonical Event that points
|
|
3045
3105
|
* back to the older Events.
|
|
3046
3106
|
*/
|
|
3047
|
-
|
|
3107
|
+
normalizeExternalMemoryDecision(candidate, matches, decision, forceConfirmation = false) {
|
|
3108
|
+
const allowed = new Set(matches.map(({ event }) => event.id));
|
|
3109
|
+
const exact = this.events.find((event) => event.status !== "forgotten" && event.status !== "archived" && externalMemoryFingerprint(event) === externalMemoryFingerprint(candidate));
|
|
3110
|
+
if (exact) allowed.add(exact.id);
|
|
3111
|
+
const existingEventIds = [...new Set((decision.existingEventIds ?? []).filter((id) => allowed.has(id)))];
|
|
3112
|
+
const requestedAction = this.normalizeExternalAction(decision.action);
|
|
3113
|
+
const missingTarget = requestedAction !== "ADD" && requestedAction !== "IGNORE" && existingEventIds.length === 0;
|
|
3114
|
+
const action = missingTarget ? "IGNORE" : requestedAction;
|
|
3115
|
+
const confidence2 = missingTarget ? 0.5 : Number.isFinite(decision.confidence) ? Math.max(0, Math.min(1, decision.confidence)) : 0.5;
|
|
3116
|
+
return {
|
|
3117
|
+
candidate: structuredClone(candidate),
|
|
3118
|
+
action,
|
|
3119
|
+
existingEventIds,
|
|
3120
|
+
matches: structuredClone([...matches]),
|
|
3121
|
+
confidence: confidence2,
|
|
3122
|
+
requiresConfirmation: forceConfirmation || confidence2 < EXTERNAL_MEMORY_AUTO_APPLY_CONFIDENCE,
|
|
3123
|
+
...decision.mergedCandidate ? { mergedCandidate: structuredClone(decision.mergedCandidate) } : {},
|
|
3124
|
+
...missingTarget ? { reason: "\u6A21\u578B\u672A\u5173\u8054\u5230\u5141\u8BB8\u8303\u56F4\u5185\u7684\u73B0\u6709\u8BB0\u5FC6\uFF0C\u5DF2\u5B89\u5168\u964D\u7EA7\u4E3A\u5FFD\u7565" } : typeof decision.reason === "string" && decision.reason.trim() ? { reason: decision.reason.trim().slice(0, 500) } : {}
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
async decideExternalMemoryCandidate(candidate, priorFingerprints, decider, topK, forceConfirmation = false) {
|
|
3128
|
+
const fingerprint = externalMemoryFingerprint(candidate);
|
|
3129
|
+
const exact = this.events.find((event) => event.status !== "forgotten" && event.status !== "archived" && externalMemoryFingerprint(event) === fingerprint);
|
|
3130
|
+
const duplicateInImport = priorFingerprints.has(fingerprint);
|
|
3131
|
+
const query = `${candidate.title} ${candidate.summary} ${(candidate.tags ?? []).join(" ")}`.trim();
|
|
3132
|
+
const matches = await this.searchEvents(query, { limit: topK, trackRetrieval: false });
|
|
3133
|
+
const decision = exact || duplicateInImport ? {
|
|
3134
|
+
action: "IGNORE",
|
|
3135
|
+
existingEventIds: exact ? [exact.id] : [],
|
|
3136
|
+
reason: exact ? "\u4E0E\u73B0\u6709\u8BB0\u5FC6\u5B8C\u5168\u91CD\u590D" : "\u4E0E\u672C\u6279\u6B21\u4E2D\u7684\u5019\u9009\u5B8C\u5168\u91CD\u590D",
|
|
3137
|
+
confidence: 1
|
|
3138
|
+
} : await decider({ candidate: structuredClone(candidate), matches: structuredClone(matches) });
|
|
3139
|
+
return this.normalizeExternalMemoryDecision(candidate, matches, decision, forceConfirmation);
|
|
3140
|
+
}
|
|
3141
|
+
async previewExternalMemoryImport(options) {
|
|
3048
3142
|
const text3 = options.text.trim();
|
|
3049
3143
|
if (!text3) throw new TypeError("External memory text must not be empty");
|
|
3050
3144
|
if (typeof options.decider !== "function") {
|
|
@@ -3055,28 +3149,198 @@ var StrataGate = class _StrataGate {
|
|
|
3055
3149
|
const extracted = await extractor({ text: text3, importedAt });
|
|
3056
3150
|
const candidates = Array.isArray(extracted?.candidates) ? extracted.candidates.slice(0, 200) : [];
|
|
3057
3151
|
const topK = Math.max(1, Math.min(20, Math.floor(options.topK ?? 5)));
|
|
3058
|
-
const
|
|
3059
|
-
const
|
|
3152
|
+
const seenFingerprints = /* @__PURE__ */ new Set();
|
|
3153
|
+
const decisions = [];
|
|
3060
3154
|
for (const candidate of candidates) {
|
|
3061
3155
|
if (!candidate || typeof candidate.title !== "string" || typeof candidate.summary !== "string") continue;
|
|
3062
|
-
const
|
|
3063
|
-
const
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3156
|
+
const fingerprint = externalMemoryFingerprint(candidate);
|
|
3157
|
+
const preview = await this.decideExternalMemoryCandidate(candidate, seenFingerprints, options.decider, topK);
|
|
3158
|
+
seenFingerprints.add(fingerprint);
|
|
3159
|
+
decisions.push(preview);
|
|
3160
|
+
}
|
|
3161
|
+
return { importedAt, baseRevision: this.revision, decisions };
|
|
3162
|
+
}
|
|
3163
|
+
getExternalMemoryImportJob(jobId) {
|
|
3164
|
+
const job = this.externalMemoryImportJobs.get(jobId.trim());
|
|
3165
|
+
return job ? structuredClone(job) : null;
|
|
3166
|
+
}
|
|
3167
|
+
async createExternalMemoryImportJob(text3) {
|
|
3168
|
+
const normalized = text3.trim();
|
|
3169
|
+
if (!normalized) throw new TypeError("External memory text must not be empty");
|
|
3170
|
+
const now = toUtc8Iso(this.now());
|
|
3171
|
+
let candidates = [];
|
|
3172
|
+
let parseError = null;
|
|
3173
|
+
try {
|
|
3174
|
+
candidates = parseExternalMemoryExport(normalized).candidates;
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
parseError = errorMessage(error).slice(0, 2e3);
|
|
3177
|
+
}
|
|
3178
|
+
const job = {
|
|
3179
|
+
id: `import_${crypto.randomUUID()}`,
|
|
3180
|
+
text: normalized,
|
|
3181
|
+
importedAt: now,
|
|
3182
|
+
status: parseError ? "extracting" : candidates.length > 0 ? "processing" : "ready",
|
|
3183
|
+
candidates: structuredClone(candidates),
|
|
3184
|
+
decisions: [],
|
|
3185
|
+
processedCount: 0,
|
|
3186
|
+
totalCount: candidates.length,
|
|
3187
|
+
recoveredFromInvalidJson: false,
|
|
3188
|
+
parseError,
|
|
3189
|
+
lastError: null,
|
|
3190
|
+
sourceBlockId: null,
|
|
3191
|
+
importedCount: 0,
|
|
3192
|
+
createdAt: now,
|
|
3193
|
+
updatedAt: now
|
|
3194
|
+
};
|
|
3195
|
+
await this.commitMutation(() => this.externalMemoryImportJobs.set(job.id, structuredClone(job)));
|
|
3196
|
+
return structuredClone(job);
|
|
3197
|
+
}
|
|
3198
|
+
async completeExternalMemoryFallback(jobId, result) {
|
|
3199
|
+
const candidates = (Array.isArray(result.candidates) ? result.candidates : []).filter((candidate) => candidate && typeof candidate.title === "string" && typeof candidate.summary === "string").slice(0, 200);
|
|
3200
|
+
return this.commitMutation(() => {
|
|
3201
|
+
const job = this.requireExternalMemoryImportJob(jobId);
|
|
3202
|
+
if (job.status !== "extracting") return structuredClone(job);
|
|
3203
|
+
job.candidates = structuredClone(candidates);
|
|
3204
|
+
job.decisions = [];
|
|
3205
|
+
job.processedCount = 0;
|
|
3206
|
+
job.totalCount = candidates.length;
|
|
3207
|
+
job.recoveredFromInvalidJson = true;
|
|
3208
|
+
job.status = candidates.length > 0 ? "processing" : "failed";
|
|
3209
|
+
job.lastError = candidates.length > 0 ? null : "\u6A21\u578B\u672A\u80FD\u4ECE\u4E0D\u5408\u683C\u5185\u5BB9\u4E2D\u6062\u590D\u51FA\u4EFB\u4F55\u5019\u9009\u8BB0\u5FC6";
|
|
3210
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3211
|
+
return structuredClone(job);
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3214
|
+
async processNextExternalMemoryImport(jobId, decider, topK = 5) {
|
|
3215
|
+
if (typeof decider !== "function") throw new TypeError("External memory decider is required");
|
|
3216
|
+
const work = await this.prepareNextExternalMemoryImport(jobId, topK);
|
|
3217
|
+
if (!work) {
|
|
3218
|
+
const current = this.requireExternalMemoryImportJob(jobId);
|
|
3219
|
+
return structuredClone(current);
|
|
3220
|
+
}
|
|
3221
|
+
const decision = work.deterministicDecision ?? await decider({
|
|
3222
|
+
candidate: structuredClone(work.candidate),
|
|
3223
|
+
matches: structuredClone(work.matches)
|
|
3224
|
+
});
|
|
3225
|
+
return this.completeNextExternalMemoryImport(
|
|
3226
|
+
work.jobId,
|
|
3227
|
+
work.index,
|
|
3228
|
+
decision,
|
|
3229
|
+
work.matches,
|
|
3230
|
+
work.forceConfirmation
|
|
3231
|
+
);
|
|
3232
|
+
}
|
|
3233
|
+
async prepareNextExternalMemoryImport(jobId, topK = 5) {
|
|
3234
|
+
const current = this.requireExternalMemoryImportJob(jobId);
|
|
3235
|
+
if (current.status !== "processing") return null;
|
|
3236
|
+
const index = current.processedCount;
|
|
3237
|
+
const candidate = current.candidates[index];
|
|
3238
|
+
if (!candidate) return null;
|
|
3239
|
+
const priorFingerprints = new Set(current.candidates.slice(0, index).map(externalMemoryFingerprint));
|
|
3240
|
+
const fingerprint = externalMemoryFingerprint(candidate);
|
|
3241
|
+
const exact = this.events.find((event) => event.status !== "forgotten" && event.status !== "archived" && externalMemoryFingerprint(event) === fingerprint);
|
|
3242
|
+
const duplicateInImport = priorFingerprints.has(fingerprint);
|
|
3243
|
+
const query = `${candidate.title} ${candidate.summary} ${(candidate.tags ?? []).join(" ")}`.trim();
|
|
3244
|
+
const matches = await this.searchEvents(query, {
|
|
3245
|
+
limit: Math.max(1, Math.min(20, Math.floor(topK))),
|
|
3246
|
+
trackRetrieval: false
|
|
3247
|
+
});
|
|
3248
|
+
const deterministicDecision = exact || duplicateInImport ? {
|
|
3249
|
+
action: "IGNORE",
|
|
3250
|
+
existingEventIds: exact ? [exact.id] : [],
|
|
3251
|
+
reason: exact ? "\u4E0E\u73B0\u6709\u8BB0\u5FC6\u5B8C\u5168\u91CD\u590D" : "\u4E0E\u672C\u6279\u6B21\u4E2D\u7684\u5019\u9009\u5B8C\u5168\u91CD\u590D",
|
|
3252
|
+
confidence: 1
|
|
3253
|
+
} : void 0;
|
|
3254
|
+
return {
|
|
3255
|
+
jobId: current.id,
|
|
3256
|
+
index,
|
|
3257
|
+
candidate: structuredClone(candidate),
|
|
3258
|
+
matches: structuredClone(matches),
|
|
3259
|
+
forceConfirmation: current.recoveredFromInvalidJson,
|
|
3260
|
+
...deterministicDecision ? { deterministicDecision } : {}
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
async completeNextExternalMemoryImport(jobId, index, decision, matches, forceConfirmation = false) {
|
|
3264
|
+
return this.commitMutation(() => {
|
|
3265
|
+
const job = this.requireExternalMemoryImportJob(jobId);
|
|
3266
|
+
if (job.processedCount > index) return structuredClone(job);
|
|
3267
|
+
if (job.status !== "processing" || job.processedCount !== index) {
|
|
3268
|
+
throw new Error(`External memory import ${jobId} is no longer at candidate ${index}`);
|
|
3269
|
+
}
|
|
3270
|
+
const candidate = job.candidates[index];
|
|
3271
|
+
if (!candidate) throw new Error(`External memory import ${jobId} has no candidate ${index}`);
|
|
3272
|
+
const normalized = this.normalizeExternalMemoryDecision(candidate, matches, decision, forceConfirmation);
|
|
3273
|
+
job.decisions.push(structuredClone(normalized));
|
|
3274
|
+
job.processedCount += 1;
|
|
3275
|
+
if (job.processedCount >= job.totalCount) {
|
|
3276
|
+
job.status = job.decisions.some(({ requiresConfirmation }) => requiresConfirmation) ? "awaiting_confirmation" : "ready";
|
|
3277
|
+
}
|
|
3278
|
+
job.lastError = null;
|
|
3279
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3280
|
+
return structuredClone(job);
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
async failExternalMemoryImportJob(jobId, error) {
|
|
3284
|
+
return this.commitMutation(() => {
|
|
3285
|
+
const job = this.requireExternalMemoryImportJob(jobId);
|
|
3286
|
+
job.status = "failed";
|
|
3287
|
+
job.lastError = errorMessage(error).slice(0, 2e3);
|
|
3288
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3289
|
+
return structuredClone(job);
|
|
3290
|
+
});
|
|
3291
|
+
}
|
|
3292
|
+
async retryExternalMemoryImportJob(jobId) {
|
|
3293
|
+
return this.commitMutation(() => {
|
|
3294
|
+
const job = this.requireExternalMemoryImportJob(jobId);
|
|
3295
|
+
if (job.status !== "failed") return structuredClone(job);
|
|
3296
|
+
job.status = job.candidates.length === 0 && job.parseError ? "extracting" : "processing";
|
|
3297
|
+
job.lastError = null;
|
|
3298
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3299
|
+
return structuredClone(job);
|
|
3300
|
+
});
|
|
3301
|
+
}
|
|
3302
|
+
async completeExternalMemoryImportJob(jobId, result) {
|
|
3303
|
+
return this.commitMutation(() => {
|
|
3304
|
+
const job = this.requireExternalMemoryImportJob(jobId);
|
|
3305
|
+
job.status = "committed";
|
|
3306
|
+
job.sourceBlockId = result.sourceBlockId;
|
|
3307
|
+
job.importedCount = result.addedEvents.length;
|
|
3308
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3309
|
+
return structuredClone(job);
|
|
3310
|
+
});
|
|
3311
|
+
}
|
|
3312
|
+
async markExternalMemoryImportUndone(jobId) {
|
|
3313
|
+
return this.commitMutation(() => {
|
|
3314
|
+
const job = this.requireExternalMemoryImportJob(jobId);
|
|
3315
|
+
job.status = "undone";
|
|
3316
|
+
job.updatedAt = toUtc8Iso(this.now());
|
|
3317
|
+
return structuredClone(job);
|
|
3318
|
+
});
|
|
3319
|
+
}
|
|
3320
|
+
async commitExternalMemoryImport(options) {
|
|
3321
|
+
const text3 = options.text.trim();
|
|
3322
|
+
if (!text3) throw new TypeError("External memory text must not be empty");
|
|
3323
|
+
if (options.candidates.length !== options.decisions.length) {
|
|
3324
|
+
throw new TypeError("External memory candidates and decisions must have the same length");
|
|
3068
3325
|
}
|
|
3069
3326
|
return this.commitMutation(() => {
|
|
3327
|
+
if (this.revision !== options.baseRevision) {
|
|
3328
|
+
throw new Error(`External memory preview is stale: expected revision ${options.baseRevision}, found ${this.revision}`);
|
|
3329
|
+
}
|
|
3330
|
+
const importedAt = toUtc8Iso(options.importedAt);
|
|
3331
|
+
const source = this.createExternalSourceBlock(text3, importedAt);
|
|
3070
3332
|
const addedEvents = [];
|
|
3071
3333
|
const changedEventIds = /* @__PURE__ */ new Set();
|
|
3072
3334
|
const decisions = [];
|
|
3073
|
-
for (const item of
|
|
3074
|
-
const action = this.normalizeExternalAction(item.
|
|
3075
|
-
const
|
|
3076
|
-
const
|
|
3335
|
+
for (const [index, item] of options.decisions.entries()) {
|
|
3336
|
+
const action = this.normalizeExternalAction(item.action);
|
|
3337
|
+
const allowed = new Set(item.matches.map(({ event }) => event.id));
|
|
3338
|
+
const targets = [...new Set(item.existingEventIds.filter((id) => allowed.has(id)))];
|
|
3339
|
+
const reason = typeof item.reason === "string" ? item.reason.trim().slice(0, 500) : void 0;
|
|
3077
3340
|
let createdEvent;
|
|
3078
|
-
const proposed = item.
|
|
3079
|
-
const
|
|
3341
|
+
const proposed = item.mergedCandidate;
|
|
3342
|
+
const original = options.candidates[index];
|
|
3343
|
+
const candidate = proposed && typeof proposed.title === "string" && typeof proposed.summary === "string" ? proposed : original;
|
|
3080
3344
|
if ((action === "ADD" || action === "MERGE" || action === "SUPERSEDE" || action === "CONFLICT") && (action === "ADD" || targets.length > 0)) {
|
|
3081
3345
|
const temporal = {
|
|
3082
3346
|
...candidate.temporal ?? {},
|
|
@@ -3102,11 +3366,12 @@ var StrataGate = class _StrataGate {
|
|
|
3102
3366
|
}
|
|
3103
3367
|
}
|
|
3104
3368
|
const audit2 = {
|
|
3105
|
-
candidate: structuredClone(
|
|
3369
|
+
candidate: structuredClone(original),
|
|
3106
3370
|
action,
|
|
3107
3371
|
existingEventIds: targets,
|
|
3108
3372
|
...createdEvent ? { createdEventId: createdEvent.id } : {},
|
|
3109
|
-
...reason ? { reason } : {}
|
|
3373
|
+
...reason ? { reason } : {},
|
|
3374
|
+
...typeof item.confidence === "number" ? { confidence: item.confidence } : {}
|
|
3110
3375
|
};
|
|
3111
3376
|
decisions.push(audit2);
|
|
3112
3377
|
}
|
|
@@ -3123,6 +3388,92 @@ var StrataGate = class _StrataGate {
|
|
|
3123
3388
|
};
|
|
3124
3389
|
});
|
|
3125
3390
|
}
|
|
3391
|
+
async importExternalMemory(options) {
|
|
3392
|
+
const preview = await this.previewExternalMemoryImport(options);
|
|
3393
|
+
return this.commitExternalMemoryImport({
|
|
3394
|
+
text: options.text,
|
|
3395
|
+
importedAt: preview.importedAt,
|
|
3396
|
+
baseRevision: preview.baseRevision,
|
|
3397
|
+
candidates: preview.decisions.map(({ candidate }) => candidate),
|
|
3398
|
+
decisions: preview.decisions
|
|
3399
|
+
});
|
|
3400
|
+
}
|
|
3401
|
+
async undoExternalMemoryImport(sourceBlockId) {
|
|
3402
|
+
const id = sourceBlockId.trim();
|
|
3403
|
+
if (!id) throw new TypeError("External memory source block ID must not be empty");
|
|
3404
|
+
return this.commitMutation(() => {
|
|
3405
|
+
const sourceIndex = this.blocks.findIndex((block) => block.id === id && block.l0Tags?.includes("external-memory-import"));
|
|
3406
|
+
if (sourceIndex < 0) throw new Error(`Unknown external memory import: ${id}`);
|
|
3407
|
+
const source = this.blocks[sourceIndex];
|
|
3408
|
+
const sourceMessageIds = new Set(source.l5Raw.map(({ id: id2 }) => id2));
|
|
3409
|
+
const importedEventIds = new Set(this.events.filter((event) => event.sourceBlockId === id).map(({ id: id2 }) => id2));
|
|
3410
|
+
const restoredEventIds = /* @__PURE__ */ new Set();
|
|
3411
|
+
const now = toUtc8Iso(this.now());
|
|
3412
|
+
this.events.splice(0, this.events.length, ...this.events.filter((event) => !importedEventIds.has(event.id)));
|
|
3413
|
+
for (const event of this.events) {
|
|
3414
|
+
for (const field of ["conflictsWithEventIds", "supersedesEventIds", "beforeEventIds", "afterEventIds", "relatedEventIds"]) {
|
|
3415
|
+
const previous = event.temporal[field] ?? [];
|
|
3416
|
+
const filtered = previous.filter((target) => !importedEventIds.has(target));
|
|
3417
|
+
if (filtered.length !== previous.length) {
|
|
3418
|
+
event.temporal[field] = filtered;
|
|
3419
|
+
restoredEventIds.add(event.id);
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
if (event.temporal.sameEventId && importedEventIds.has(event.temporal.sameEventId)) {
|
|
3423
|
+
delete event.temporal.sameEventId;
|
|
3424
|
+
restoredEventIds.add(event.id);
|
|
3425
|
+
}
|
|
3426
|
+
if (event.supersededBy && importedEventIds.has(event.supersededBy)) {
|
|
3427
|
+
const replacement = this.events.find((candidate) => candidate.id !== event.id && (candidate.temporal.supersedesEventIds ?? []).includes(event.id));
|
|
3428
|
+
event.status = replacement ? "superseded" : "active";
|
|
3429
|
+
event.supersededBy = replacement?.id ?? null;
|
|
3430
|
+
if (!replacement && event.weight.forcedCap === 0.1) event.weight.forcedCap = null;
|
|
3431
|
+
restoredEventIds.add(event.id);
|
|
3432
|
+
}
|
|
3433
|
+
if (restoredEventIds.has(event.id)) event.updatedAt = now;
|
|
3434
|
+
}
|
|
3435
|
+
for (const [jobId, job] of this.elementProjectionJobs) {
|
|
3436
|
+
if (job.sourceEventIds.some((eventId) => importedEventIds.has(eventId))) this.elementProjectionJobs.delete(jobId);
|
|
3437
|
+
}
|
|
3438
|
+
for (const [jobId, job] of this.graphProjectionJobs) {
|
|
3439
|
+
if (job.sourceEventIds.some((eventId) => importedEventIds.has(eventId))) this.graphProjectionJobs.delete(jobId);
|
|
3440
|
+
}
|
|
3441
|
+
for (const element of this.elements) {
|
|
3442
|
+
element.sourceEventIds = element.sourceEventIds.filter((eventId) => !importedEventIds.has(eventId));
|
|
3443
|
+
element.sourceMessageIds = element.sourceMessageIds.filter((messageId) => !sourceMessageIds.has(messageId));
|
|
3444
|
+
element.facts = element.facts.flatMap((fact) => {
|
|
3445
|
+
fact.sourceEventIds = fact.sourceEventIds.filter((eventId) => !importedEventIds.has(eventId));
|
|
3446
|
+
return fact.sourceEventIds.length > 0 ? [fact] : [];
|
|
3447
|
+
});
|
|
3448
|
+
const current = [...element.facts].reverse().find((fact) => fact.status === "active" && fact.mode === "state");
|
|
3449
|
+
element.currentState = current ? Array.isArray(current.value) ? current.value.join("\u3001") : current.value : "";
|
|
3450
|
+
}
|
|
3451
|
+
this.elements.splice(0, this.elements.length, ...this.elements.filter((element) => element.sourceEventIds.length > 0 || element.facts.length > 0));
|
|
3452
|
+
for (const node of this.graphNodes) {
|
|
3453
|
+
node.sourceEventIds = node.sourceEventIds.filter((eventId) => !importedEventIds.has(eventId));
|
|
3454
|
+
node.facts = node.facts.flatMap((fact) => {
|
|
3455
|
+
fact.sourceEventIds = fact.sourceEventIds.filter((eventId) => !importedEventIds.has(eventId));
|
|
3456
|
+
return fact.sourceEventIds.length > 0 ? [fact] : [];
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3459
|
+
const removedNodeIds = new Set(this.graphNodes.filter((node) => node.sourceEventIds.length === 0 && node.facts.length === 0).map(({ id: id2 }) => id2));
|
|
3460
|
+
this.graphNodes.splice(0, this.graphNodes.length, ...this.graphNodes.filter((node) => !removedNodeIds.has(node.id)));
|
|
3461
|
+
for (const edge of this.graphEdges) {
|
|
3462
|
+
edge.sourceEventIds = edge.sourceEventIds.filter((eventId) => !importedEventIds.has(eventId));
|
|
3463
|
+
}
|
|
3464
|
+
this.graphEdges.splice(0, this.graphEdges.length, ...this.graphEdges.filter((edge) => edge.sourceEventIds.length > 0 && !removedNodeIds.has(edge.fromNodeId) && !removedNodeIds.has(edge.toNodeId)));
|
|
3465
|
+
for (const [receiptId, receipt] of this.usageReceipts) {
|
|
3466
|
+
receipt.eventIds = receipt.eventIds.filter((eventId) => !importedEventIds.has(eventId));
|
|
3467
|
+
if (receipt.eventIds.length === 0 && receipt.elementIds.length === 0) this.usageReceipts.delete(receiptId);
|
|
3468
|
+
}
|
|
3469
|
+
this.blocks.splice(sourceIndex, 1);
|
|
3470
|
+
return {
|
|
3471
|
+
sourceBlockId: id,
|
|
3472
|
+
removedEventIds: [...importedEventIds],
|
|
3473
|
+
restoredEventIds: [...restoredEventIds]
|
|
3474
|
+
};
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3126
3477
|
async searchEvents(query, options = {}) {
|
|
3127
3478
|
const limit = Math.max(1, Math.min(20, options.limit ?? 6));
|
|
3128
3479
|
const participants = (options.participants ?? []).map(normalizeSearchText).filter(Boolean);
|
|
@@ -3176,7 +3527,7 @@ var StrataGate = class _StrataGate {
|
|
|
3176
3527
|
rankings.push(structured(candidates));
|
|
3177
3528
|
}
|
|
3178
3529
|
const ranked = rrfRank(rankings).slice(0, limit).map(({ item: event, score }) => ({ event, score }));
|
|
3179
|
-
if (ranked.length > 0) {
|
|
3530
|
+
if (ranked.length > 0 && options.trackRetrieval !== false) {
|
|
3180
3531
|
const now = toUtc8Iso(this.now());
|
|
3181
3532
|
await this.commitMutation(() => {
|
|
3182
3533
|
for (const { event } of ranked) event.weight.lastRetrievedAt = now;
|
|
@@ -3513,6 +3864,11 @@ var StrataGate = class _StrataGate {
|
|
|
3513
3864
|
const action = typeof value === "string" ? value.trim().toUpperCase() : "";
|
|
3514
3865
|
return action === "ADD" || action === "MERGE" || action === "SUPERSEDE" || action === "CONFLICT" || action === "IGNORE" ? action : "IGNORE";
|
|
3515
3866
|
}
|
|
3867
|
+
requireExternalMemoryImportJob(id) {
|
|
3868
|
+
const job = this.externalMemoryImportJobs.get(id.trim());
|
|
3869
|
+
if (!job) throw new Error(`Unknown external memory import job: ${id}`);
|
|
3870
|
+
return job;
|
|
3871
|
+
}
|
|
3516
3872
|
createExternalSourceBlock(text3, importedAt) {
|
|
3517
3873
|
const blockId = this.idFactory("blk");
|
|
3518
3874
|
const threadId = `external-import:${blockId}`;
|
|
@@ -3527,7 +3883,7 @@ var StrataGate = class _StrataGate {
|
|
|
3527
3883
|
const block = {
|
|
3528
3884
|
id: blockId,
|
|
3529
3885
|
threadId,
|
|
3530
|
-
sequence: this.blocks.
|
|
3886
|
+
sequence: Math.max(0, ...this.blocks.map(({ sequence }) => sequence)) + 1,
|
|
3531
3887
|
startTurn: 1,
|
|
3532
3888
|
endTurn: 1,
|
|
3533
3889
|
createdAt: importedAt,
|
|
@@ -4059,6 +4415,8 @@ var StrataGate = class _StrataGate {
|
|
|
4059
4415
|
for (const receipt of copy.usageReceipts) this.usageReceipts.set(receipt.id, receipt);
|
|
4060
4416
|
this.ingestionReceipts.clear();
|
|
4061
4417
|
for (const receipt of copy.ingestionReceipts) this.ingestionReceipts.set(receipt.id, receipt);
|
|
4418
|
+
this.externalMemoryImportJobs.clear();
|
|
4419
|
+
for (const job of copy.externalMemoryImportJobs) this.externalMemoryImportJobs.set(job.id, job);
|
|
4062
4420
|
this.successfulModelResponses.splice(0, this.successfulModelResponses.length, ...copy.successfulModelResponses ?? []);
|
|
4063
4421
|
this.validateReferences();
|
|
4064
4422
|
}
|
|
@@ -4327,7 +4685,9 @@ var STRUCTURED_FIELDS = {
|
|
|
4327
4685
|
summarizer: ["l0Title", "l0Tags", "l1Summary", "l2Keypoints", "shouldExtract"],
|
|
4328
4686
|
extractor: ["shouldExtract", "reason", "events"],
|
|
4329
4687
|
projector: ["reason", "changes"],
|
|
4330
|
-
graphProjector: ["reason", "nodes", "edges"]
|
|
4688
|
+
graphProjector: ["reason", "nodes", "edges"],
|
|
4689
|
+
externalMemoryExtractor: ["reason", "candidates"],
|
|
4690
|
+
externalMemoryDecider: ["action", "reason", "confidence"]
|
|
4331
4691
|
};
|
|
4332
4692
|
var STRING_ARRAY = { type: "array", items: { type: "string" } };
|
|
4333
4693
|
var OPEN_OBJECT = { type: "object", additionalProperties: true };
|
|
@@ -4435,6 +4795,17 @@ var GRAPH_PROJECTOR_PARAMETERS = {
|
|
|
4435
4795
|
nodes: { type: "array", items: GRAPH_NODE, required: true },
|
|
4436
4796
|
edges: { type: "array", items: GRAPH_EDGE, required: true }
|
|
4437
4797
|
};
|
|
4798
|
+
var EXTERNAL_MEMORY_DECIDER_PARAMETERS = {
|
|
4799
|
+
action: { type: "string", enum: ["ADD", "MERGE", "SUPERSEDE", "CONFLICT", "IGNORE"], required: true },
|
|
4800
|
+
existingEventIds: STRING_ARRAY,
|
|
4801
|
+
mergedCandidate: OPEN_OBJECT,
|
|
4802
|
+
reason: { type: "string", required: true },
|
|
4803
|
+
confidence: { type: "number", required: true }
|
|
4804
|
+
};
|
|
4805
|
+
var EXTERNAL_MEMORY_EXTRACTOR_PARAMETERS = {
|
|
4806
|
+
reason: { type: "string", required: true },
|
|
4807
|
+
candidates: { type: "array", items: OPEN_OBJECT, required: true }
|
|
4808
|
+
};
|
|
4438
4809
|
var STRUCTURED_TOOLS = {
|
|
4439
4810
|
summarizer: {
|
|
4440
4811
|
name: "stratagate_summarize_block",
|
|
@@ -4455,6 +4826,16 @@ var STRUCTURED_TOOLS = {
|
|
|
4455
4826
|
name: "stratagate_project_knowledge_graph",
|
|
4456
4827
|
description: "Project stable graph nodes and directed edges from supplied event evidence.",
|
|
4457
4828
|
parameters: GRAPH_PROJECTOR_PARAMETERS
|
|
4829
|
+
},
|
|
4830
|
+
externalMemoryDecider: {
|
|
4831
|
+
name: "stratagate_decide_external_memory",
|
|
4832
|
+
description: "Decide how one external memory candidate relates to retrieved local events.",
|
|
4833
|
+
parameters: EXTERNAL_MEMORY_DECIDER_PARAMETERS
|
|
4834
|
+
},
|
|
4835
|
+
externalMemoryExtractor: {
|
|
4836
|
+
name: "stratagate_recover_external_memory",
|
|
4837
|
+
description: "Recover structured external-memory candidates from malformed JSON or plain text.",
|
|
4838
|
+
parameters: EXTERNAL_MEMORY_EXTRACTOR_PARAMETERS
|
|
4458
4839
|
}
|
|
4459
4840
|
};
|
|
4460
4841
|
function toolSchema(kind) {
|
|
@@ -4483,7 +4864,10 @@ var DshModelBridge = class {
|
|
|
4483
4864
|
successfulResponses = [];
|
|
4484
4865
|
offCapabilities = /* @__PURE__ */ new Map();
|
|
4485
4866
|
run(session, operation) {
|
|
4486
|
-
return this.sessions.run(session, operation);
|
|
4867
|
+
return this.sessions.run({ session, sessionId: session.id }, operation);
|
|
4868
|
+
}
|
|
4869
|
+
runDetached(sessionId, operation) {
|
|
4870
|
+
return this.sessions.run({ sessionId }, operation);
|
|
4487
4871
|
}
|
|
4488
4872
|
takeSuccessfulResponses() {
|
|
4489
4873
|
const responses = this.successfulResponses.splice(0, this.successfulResponses.length);
|
|
@@ -4625,10 +5009,43 @@ var DshModelBridge = class {
|
|
|
4625
5009
|
});
|
|
4626
5010
|
return { reason: text2(raw.reason, "Projected Event evidence into the Knowledge Graph."), nodes, edges };
|
|
4627
5011
|
};
|
|
5012
|
+
externalMemoryDecider = async (context) => {
|
|
5013
|
+
const raw = object(await this.callStructured(
|
|
5014
|
+
"externalMemoryDecider",
|
|
5015
|
+
`${EXTERNAL_MEMORY_DECIDER_PROMPT_ZH_CN}
|
|
5016
|
+
|
|
5017
|
+
\u8C03\u7528 ${STRUCTURED_TOOLS.externalMemoryDecider.name} \u6070\u597D\u4E00\u6B21\uFF0C\u4E0D\u8981\u8FD4\u56DE\u666E\u901A\u6587\u672C\u3002confidence \u5FC5\u987B\u662F 0 \u5230 1\uFF0C\u8868\u793A\u8BE5 action \u5224\u65AD\u7684\u628A\u63E1\u7A0B\u5EA6\u3002`,
|
|
5018
|
+
context
|
|
5019
|
+
));
|
|
5020
|
+
const action = text2(raw.action).toUpperCase();
|
|
5021
|
+
const allowedActions = /* @__PURE__ */ new Set(["ADD", "MERGE", "SUPERSEDE", "CONFLICT", "IGNORE"]);
|
|
5022
|
+
const proposed = object(raw.mergedCandidate);
|
|
5023
|
+
const mergedCandidate = text2(proposed.title) && text2(proposed.summary) ? proposed : void 0;
|
|
5024
|
+
return {
|
|
5025
|
+
action: allowedActions.has(action) ? action : "IGNORE",
|
|
5026
|
+
existingEventIds: strings2(raw.existingEventIds),
|
|
5027
|
+
...mergedCandidate ? { mergedCandidate } : {},
|
|
5028
|
+
reason: text2(raw.reason, "\u6A21\u578B\u672A\u63D0\u4F9B\u88C1\u51B3\u7406\u7531\u3002").slice(0, 500),
|
|
5029
|
+
confidence: typeof raw.confidence === "number" ? Math.max(0, Math.min(1, raw.confidence)) : 0.5
|
|
5030
|
+
};
|
|
5031
|
+
};
|
|
5032
|
+
externalMemoryExtractor = async ({ text: source, importedAt }) => {
|
|
5033
|
+
const raw = object(await this.callStructured(
|
|
5034
|
+
"externalMemoryExtractor",
|
|
5035
|
+
`Recover durable memory candidates from the supplied malformed external-memory export. Use only facts present in sourceText; never invent missing facts. Preserve uncertainty and omit unsupported dates. Call ${STRUCTURED_TOOLS.externalMemoryExtractor.name} exactly once with reason and candidates. Each candidate needs a concise title and self-contained summary. Do not return ordinary text.`,
|
|
5036
|
+
{ sourceText: source, importedAt }
|
|
5037
|
+
));
|
|
5038
|
+
const parsed = parseExternalMemoryExport(JSON.stringify({
|
|
5039
|
+
schemaVersion: "stratagate.external-memory.v2",
|
|
5040
|
+
sourceType: "external_ai_memory_export",
|
|
5041
|
+
candidates: Array.isArray(raw.candidates) ? raw.candidates : []
|
|
5042
|
+
}));
|
|
5043
|
+
return { candidates: parsed.candidates, reason: text2(raw.reason, parsed.reason) };
|
|
5044
|
+
};
|
|
4628
5045
|
async callStructured(kind, system, payload) {
|
|
4629
|
-
const
|
|
4630
|
-
if (!
|
|
4631
|
-
const baseRoute = this.resolveRoute(session);
|
|
5046
|
+
const execution = this.sessions.getStore();
|
|
5047
|
+
if (!execution) throw new Error("StrataGate model callback ran without an execution context");
|
|
5048
|
+
const baseRoute = this.resolveRoute(execution.session);
|
|
4632
5049
|
const routeKey = `${baseRoute.provider}\0${baseRoute.model}`;
|
|
4633
5050
|
let useOff = await this.shouldUseOff(baseRoute);
|
|
4634
5051
|
let lastError;
|
|
@@ -4656,7 +5073,7 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
4656
5073
|
function: { name: STRUCTURED_TOOLS[kind].name }
|
|
4657
5074
|
},
|
|
4658
5075
|
maxTokens: this.config.maxOutputTokens,
|
|
4659
|
-
sessionId:
|
|
5076
|
+
sessionId: execution.sessionId,
|
|
4660
5077
|
purpose: "compaction"
|
|
4661
5078
|
};
|
|
4662
5079
|
try {
|
|
@@ -4805,7 +5222,7 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
4805
5222
|
}
|
|
4806
5223
|
}
|
|
4807
5224
|
resolveRoute(session) {
|
|
4808
|
-
const request = session
|
|
5225
|
+
const request = session?.requestHeader()?.config;
|
|
4809
5226
|
if (this.config.provider && this.config.model) {
|
|
4810
5227
|
return { provider: this.config.provider, model: this.config.model };
|
|
4811
5228
|
}
|
|
@@ -5052,6 +5469,7 @@ var StrataGateRuntime = class {
|
|
|
5052
5469
|
derivationTimers = /* @__PURE__ */ new Map();
|
|
5053
5470
|
derivationRuns = /* @__PURE__ */ new Map();
|
|
5054
5471
|
adminSnapshotCache = /* @__PURE__ */ new Map();
|
|
5472
|
+
externalImportRuns = /* @__PURE__ */ new Map();
|
|
5055
5473
|
ingestTail = Promise.resolve();
|
|
5056
5474
|
settingsTail = Promise.resolve();
|
|
5057
5475
|
batchSequence = 0;
|
|
@@ -5393,6 +5811,7 @@ var StrataGateRuntime = class {
|
|
|
5393
5811
|
}
|
|
5394
5812
|
const settled = await Promise.allSettled(this.spaces.values());
|
|
5395
5813
|
await Promise.allSettled(this.derivationRuns.values());
|
|
5814
|
+
await Promise.allSettled(this.externalImportRuns.values());
|
|
5396
5815
|
await Promise.all(settled.flatMap((result) => result.status === "fulfilled" ? [result.value.close()] : []));
|
|
5397
5816
|
if (flushError !== void 0) throw flushError;
|
|
5398
5817
|
}
|
|
@@ -5500,36 +5919,213 @@ var StrataGateRuntime = class {
|
|
|
5500
5919
|
await storage.close();
|
|
5501
5920
|
}
|
|
5502
5921
|
}
|
|
5503
|
-
|
|
5504
|
-
|
|
5922
|
+
externalImportView(job) {
|
|
5923
|
+
return {
|
|
5924
|
+
jobId: job.id,
|
|
5925
|
+
status: job.status,
|
|
5926
|
+
processedCount: job.processedCount,
|
|
5927
|
+
totalCount: job.totalCount,
|
|
5928
|
+
recoveredFromInvalidJson: job.recoveredFromInvalidJson,
|
|
5929
|
+
parseError: job.parseError,
|
|
5930
|
+
lastError: job.lastError,
|
|
5931
|
+
sourceBlockId: job.sourceBlockId,
|
|
5932
|
+
importedCount: job.importedCount,
|
|
5933
|
+
createdAt: job.createdAt,
|
|
5934
|
+
updatedAt: job.updatedAt,
|
|
5935
|
+
decisions: job.decisions.map(({ matches: _matches, mergedCandidate: _mergedCandidate, ...decision }) => decision),
|
|
5936
|
+
requiresConfirmationCount: job.decisions.filter(({ requiresConfirmation }) => requiresConfirmation).length
|
|
5937
|
+
};
|
|
5938
|
+
}
|
|
5939
|
+
async refreshExternalImportMemory(namespace, memory) {
|
|
5940
|
+
if (this.config.database === ":memory:" || !existsSync(this.config.database)) return;
|
|
5941
|
+
const storage = new SqliteStorage({ filename: this.config.database, readonly: true });
|
|
5942
|
+
try {
|
|
5943
|
+
const head = storage.listNamespaceRevisions().find((entry) => entry.namespace === namespace);
|
|
5944
|
+
if (head && head.revision !== memory.storageRevision) await memory.reloadFromStorage();
|
|
5945
|
+
} finally {
|
|
5946
|
+
await storage.close();
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
async refreshActiveExternalImportMemory(namespace) {
|
|
5950
|
+
const opening = this.spaces.get(namespace);
|
|
5951
|
+
if (!opening) return;
|
|
5952
|
+
await (await opening).reloadFromStorage();
|
|
5953
|
+
}
|
|
5954
|
+
async retryExternalImportWrite(namespace, operation) {
|
|
5955
|
+
let lastConflict;
|
|
5956
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
5957
|
+
const { memory, owned } = await this.openAdminMemory(namespace);
|
|
5958
|
+
try {
|
|
5959
|
+
await this.refreshExternalImportMemory(namespace, memory);
|
|
5960
|
+
const result = await operation(memory);
|
|
5961
|
+
if (owned) await this.refreshActiveExternalImportMemory(namespace);
|
|
5962
|
+
return result;
|
|
5963
|
+
} catch (error) {
|
|
5964
|
+
if (!(error instanceof StorageConflictError)) throw error;
|
|
5965
|
+
lastConflict = error;
|
|
5966
|
+
if (!owned) await memory.reloadFromStorage();
|
|
5967
|
+
} finally {
|
|
5968
|
+
if (owned) await memory.close();
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
5971
|
+
throw lastConflict ?? new Error(`Unable to update external memory import ${namespace}`);
|
|
5972
|
+
}
|
|
5973
|
+
takeSuccessfulResponses() {
|
|
5974
|
+
return typeof this.models.takeSuccessfulResponses === "function" ? this.models.takeSuccessfulResponses() : [];
|
|
5975
|
+
}
|
|
5976
|
+
async persistExternalImportResponses(namespace, responses) {
|
|
5977
|
+
if (responses.length === 0) return;
|
|
5978
|
+
await this.retryExternalImportWrite(namespace, (memory) => memory.recordSuccessfulModelResponses(responses));
|
|
5979
|
+
}
|
|
5980
|
+
scheduleExternalMemoryImport(namespace, jobId) {
|
|
5981
|
+
if (this.closed || this.externalImportRuns.has(jobId)) return;
|
|
5982
|
+
const run = (async () => {
|
|
5983
|
+
while (!this.closed) {
|
|
5984
|
+
let job = null;
|
|
5985
|
+
let work = null;
|
|
5986
|
+
const prepared = await this.openAdminMemory(namespace);
|
|
5987
|
+
try {
|
|
5988
|
+
await this.refreshExternalImportMemory(namespace, prepared.memory);
|
|
5989
|
+
job = prepared.memory.getExternalMemoryImportJob(jobId);
|
|
5990
|
+
if (!job) return;
|
|
5991
|
+
if (job.status === "processing") {
|
|
5992
|
+
work = await prepared.memory.prepareNextExternalMemoryImport(jobId);
|
|
5993
|
+
} else if (job.status !== "extracting") return;
|
|
5994
|
+
} finally {
|
|
5995
|
+
if (prepared.owned) await prepared.memory.close();
|
|
5996
|
+
}
|
|
5997
|
+
try {
|
|
5998
|
+
if (job.status === "extracting") {
|
|
5999
|
+
const recovered = await this.models.runDetached(`admin-import-recovery:${jobId}`, () => this.models.externalMemoryExtractor({ text: job.text, importedAt: job.importedAt }));
|
|
6000
|
+
const responses2 = this.takeSuccessfulResponses();
|
|
6001
|
+
await this.retryExternalImportWrite(namespace, (memory) => memory.completeExternalMemoryFallback(jobId, recovered));
|
|
6002
|
+
await this.persistExternalImportResponses(namespace, responses2);
|
|
6003
|
+
continue;
|
|
6004
|
+
}
|
|
6005
|
+
if (!work) return;
|
|
6006
|
+
let decision;
|
|
6007
|
+
let responses = [];
|
|
6008
|
+
if (work.deterministicDecision) {
|
|
6009
|
+
decision = work.deterministicDecision;
|
|
6010
|
+
} else {
|
|
6011
|
+
decision = await this.models.runDetached(`admin-import:${jobId}`, () => this.models.externalMemoryDecider({
|
|
6012
|
+
candidate: structuredClone(work.candidate),
|
|
6013
|
+
matches: structuredClone(work.matches)
|
|
6014
|
+
}));
|
|
6015
|
+
responses = this.takeSuccessfulResponses();
|
|
6016
|
+
}
|
|
6017
|
+
await this.retryExternalImportWrite(namespace, (memory) => memory.completeNextExternalMemoryImport(
|
|
6018
|
+
work.jobId,
|
|
6019
|
+
work.index,
|
|
6020
|
+
decision,
|
|
6021
|
+
work.matches,
|
|
6022
|
+
work.forceConfirmation
|
|
6023
|
+
));
|
|
6024
|
+
await this.persistExternalImportResponses(namespace, responses);
|
|
6025
|
+
} catch (error) {
|
|
6026
|
+
if (error instanceof StorageConflictError) {
|
|
6027
|
+
this.onIngestError(error);
|
|
6028
|
+
return;
|
|
6029
|
+
}
|
|
6030
|
+
try {
|
|
6031
|
+
await this.retryExternalImportWrite(namespace, (memory) => memory.failExternalMemoryImportJob(jobId, error));
|
|
6032
|
+
} catch (persistError) {
|
|
6033
|
+
this.onIngestError(persistError);
|
|
6034
|
+
}
|
|
6035
|
+
return;
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
})().catch((error) => this.onIngestError(error)).finally(() => {
|
|
6039
|
+
this.externalImportRuns.delete(jobId);
|
|
6040
|
+
});
|
|
6041
|
+
this.externalImportRuns.set(jobId, run);
|
|
6042
|
+
}
|
|
6043
|
+
/** Create a durable analysis job and return before model-backed work begins. */
|
|
6044
|
+
async adminPreviewExternalMemory(namespace, text3) {
|
|
5505
6045
|
const key = namespace.trim();
|
|
5506
6046
|
if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
|
|
6047
|
+
if (!text3.trim()) throw new TypeError("External memory text must not be empty");
|
|
5507
6048
|
await this.flush();
|
|
5508
|
-
const
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
6049
|
+
const { memory, owned } = await this.openAdminMemory(key);
|
|
6050
|
+
try {
|
|
6051
|
+
await this.refreshExternalImportMemory(key, memory);
|
|
6052
|
+
const job = await memory.createExternalMemoryImportJob(text3);
|
|
6053
|
+
this.scheduleExternalMemoryImport(key, job.id);
|
|
6054
|
+
return this.externalImportView(job);
|
|
6055
|
+
} finally {
|
|
6056
|
+
if (owned) await memory.close();
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
async adminExternalMemoryStatus(namespace, jobId) {
|
|
6060
|
+
const key = namespace.trim();
|
|
6061
|
+
if (!key) throw new TypeError("StrataGate admin namespace must not be empty");
|
|
6062
|
+
const { memory, owned } = await this.openAdminMemory(key);
|
|
6063
|
+
try {
|
|
6064
|
+
await this.refreshExternalImportMemory(key, memory);
|
|
6065
|
+
const jobs = memory.listExternalMemoryImportJobs();
|
|
6066
|
+
const job = jobId ? jobs.find(({ id }) => id === jobId) : [...jobs].sort((left, right) => right.createdAt.localeCompare(left.createdAt))[0];
|
|
6067
|
+
if (!job) return { job: null };
|
|
6068
|
+
if (job.status === "extracting" || job.status === "processing") this.scheduleExternalMemoryImport(key, job.id);
|
|
6069
|
+
return this.externalImportView(job);
|
|
6070
|
+
} finally {
|
|
6071
|
+
if (owned) await memory.close();
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
async adminRetryExternalMemory(namespace, jobId) {
|
|
6075
|
+
const key = namespace.trim();
|
|
6076
|
+
let lastConflict;
|
|
6077
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
6078
|
+
const { memory, owned } = await this.openAdminMemory(key);
|
|
6079
|
+
try {
|
|
6080
|
+
await this.refreshExternalImportMemory(key, memory);
|
|
6081
|
+
const job = await memory.retryExternalMemoryImportJob(jobId);
|
|
6082
|
+
this.scheduleExternalMemoryImport(key, job.id);
|
|
6083
|
+
return this.externalImportView(job);
|
|
6084
|
+
} catch (error) {
|
|
6085
|
+
if (!(error instanceof StorageConflictError)) throw error;
|
|
6086
|
+
lastConflict = error;
|
|
6087
|
+
if (!owned) await memory.reloadFromStorage();
|
|
6088
|
+
} finally {
|
|
6089
|
+
if (owned) await memory.close();
|
|
5516
6090
|
}
|
|
5517
|
-
memory = await StrataGate.open({
|
|
5518
|
-
database: this.config.database,
|
|
5519
|
-
namespace: key,
|
|
5520
|
-
blockTurnSize: this.blockTurnSize,
|
|
5521
|
-
blockDecayLambda: this.blockDecayLambda,
|
|
5522
|
-
graphProjector: this.models.graphProjector,
|
|
5523
|
-
disableElementProjection: true
|
|
5524
|
-
});
|
|
5525
|
-
owned = true;
|
|
5526
6091
|
}
|
|
6092
|
+
throw lastConflict;
|
|
6093
|
+
}
|
|
6094
|
+
async adminCommitExternalMemory(namespace, jobId, choices) {
|
|
6095
|
+
const key = namespace.trim();
|
|
6096
|
+
const { memory, owned } = await this.openAdminMemory(key);
|
|
5527
6097
|
try {
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
6098
|
+
await this.refreshExternalImportMemory(key, memory);
|
|
6099
|
+
const job = memory.getExternalMemoryImportJob(jobId);
|
|
6100
|
+
if (!job) throw new Error("\u627E\u4E0D\u5230\u5BFC\u5165\u4EFB\u52A1");
|
|
6101
|
+
if (job.status !== "ready" && job.status !== "awaiting_confirmation") {
|
|
6102
|
+
throw new Error("\u5BFC\u5165\u5206\u6790\u5C1A\u672A\u5B8C\u6210");
|
|
6103
|
+
}
|
|
6104
|
+
const selected = new Map(choices.filter(({ index, action }) => Number.isSafeInteger(index) && ["ADD", "MERGE", "SUPERSEDE", "CONFLICT", "IGNORE"].includes(action)).map(({ index, action }) => [index, action]));
|
|
6105
|
+
const decisions = job.decisions.map((decision, index) => {
|
|
6106
|
+
if (!decision.requiresConfirmation) return decision;
|
|
6107
|
+
const action = selected.get(index) ?? "IGNORE";
|
|
6108
|
+
const needsTarget = action === "MERGE" || action === "SUPERSEDE" || action === "CONFLICT";
|
|
6109
|
+
if (needsTarget && decision.existingEventIds.length === 0) {
|
|
6110
|
+
return { ...decision, action: "IGNORE", existingEventIds: [], reason: "\u7528\u6237\u9009\u62E9\u7684\u64CD\u4F5C\u6CA1\u6709\u53EF\u5173\u8054\u65E7\u8BB0\u5FC6\uFF0C\u5DF2\u5B89\u5168\u5FFD\u7565" };
|
|
6111
|
+
}
|
|
6112
|
+
return {
|
|
6113
|
+
...decision,
|
|
6114
|
+
action,
|
|
6115
|
+
existingEventIds: action === "ADD" || action === "IGNORE" ? [] : decision.existingEventIds,
|
|
6116
|
+
reason: `\u7528\u6237\u786E\u8BA4\uFF1A${action}`
|
|
6117
|
+
};
|
|
5531
6118
|
});
|
|
6119
|
+
const result = await memory.commitExternalMemoryImport({
|
|
6120
|
+
text: job.text,
|
|
6121
|
+
importedAt: job.importedAt,
|
|
6122
|
+
baseRevision: memory.storageRevision,
|
|
6123
|
+
candidates: job.candidates,
|
|
6124
|
+
decisions
|
|
6125
|
+
});
|
|
6126
|
+
const completed = await memory.completeExternalMemoryImportJob(job.id, result);
|
|
5532
6127
|
return {
|
|
6128
|
+
...this.externalImportView(completed),
|
|
5533
6129
|
sourceBlockId: result.sourceBlockId,
|
|
5534
6130
|
decisions: result.decisions,
|
|
5535
6131
|
importedCount: result.addedEvents.length,
|
|
@@ -5539,6 +6135,19 @@ var StrataGateRuntime = class {
|
|
|
5539
6135
|
if (owned) await memory.close();
|
|
5540
6136
|
}
|
|
5541
6137
|
}
|
|
6138
|
+
async adminUndoExternalMemory(namespace, sourceBlockId) {
|
|
6139
|
+
const key = namespace.trim();
|
|
6140
|
+
const { memory, owned } = await this.openAdminMemory(key);
|
|
6141
|
+
try {
|
|
6142
|
+
await this.refreshExternalImportMemory(key, memory);
|
|
6143
|
+
const result = await memory.undoExternalMemoryImport(sourceBlockId);
|
|
6144
|
+
const job = memory.listExternalMemoryImportJobs().find((candidate) => candidate.sourceBlockId === sourceBlockId);
|
|
6145
|
+
if (job) await memory.markExternalMemoryImportUndone(job.id);
|
|
6146
|
+
return result;
|
|
6147
|
+
} finally {
|
|
6148
|
+
if (owned) await memory.close();
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
5542
6151
|
adminWorkspaceName(namespace) {
|
|
5543
6152
|
const remembered = this.workspaceNames.get(namespace);
|
|
5544
6153
|
if (remembered) return remembered;
|
|
@@ -5784,6 +6393,24 @@ var StrataGateRuntime = class {
|
|
|
5784
6393
|
timer.unref?.();
|
|
5785
6394
|
this.migrationTimers.set(namespace, timer);
|
|
5786
6395
|
}
|
|
6396
|
+
async openAdminMemory(namespace) {
|
|
6397
|
+
const active = this.spaces.get(namespace);
|
|
6398
|
+
if (active) return { memory: await active, owned: false };
|
|
6399
|
+
if (this.config.database === ":memory:" || !existsSync(this.config.database)) {
|
|
6400
|
+
throw new Error(`Unknown StrataGate namespace: ${namespace}`);
|
|
6401
|
+
}
|
|
6402
|
+
return {
|
|
6403
|
+
memory: await StrataGate.open({
|
|
6404
|
+
database: this.config.database,
|
|
6405
|
+
namespace,
|
|
6406
|
+
blockTurnSize: this.blockTurnSize,
|
|
6407
|
+
blockDecayLambda: this.blockDecayLambda,
|
|
6408
|
+
graphProjector: this.models.graphProjector,
|
|
6409
|
+
disableElementProjection: true
|
|
6410
|
+
}),
|
|
6411
|
+
owned: true
|
|
6412
|
+
};
|
|
6413
|
+
}
|
|
5787
6414
|
rememberWorkspace(namespace, cwd) {
|
|
5788
6415
|
const name2 = workspaceDisplayName(cwd);
|
|
5789
6416
|
this.workspaceNames.set(namespace, name2);
|
|
@@ -6654,10 +7281,41 @@ async function importExternalMemory(runtime, req) {
|
|
|
6654
7281
|
throw new AdminHttpError(400, "\u5BFC\u5165\u8BF7\u6C42\u7F3A\u5C11 JSON body");
|
|
6655
7282
|
}
|
|
6656
7283
|
const namespace = typeof body.namespace === "string" ? body.namespace.trim() : "";
|
|
6657
|
-
const text3 = typeof body.text === "string" ? body.text.trim() : "";
|
|
6658
7284
|
if (!namespace) throw new AdminHttpError(400, "namespace is required");
|
|
6659
|
-
|
|
6660
|
-
|
|
7285
|
+
const operation = typeof body.operation === "string" ? body.operation : "preview";
|
|
7286
|
+
if (operation === "preview") {
|
|
7287
|
+
const text3 = typeof body.text === "string" ? body.text.trim() : "";
|
|
7288
|
+
if (!text3) throw new AdminHttpError(400, "text is required");
|
|
7289
|
+
return runtime.adminPreviewExternalMemory(namespace, text3);
|
|
7290
|
+
}
|
|
7291
|
+
if (operation === "status") {
|
|
7292
|
+
const jobId = typeof body.jobId === "string" ? body.jobId.trim() : void 0;
|
|
7293
|
+
return runtime.adminExternalMemoryStatus(namespace, jobId);
|
|
7294
|
+
}
|
|
7295
|
+
if (operation === "retry") {
|
|
7296
|
+
const jobId = typeof body.jobId === "string" ? body.jobId.trim() : "";
|
|
7297
|
+
if (!jobId) throw new AdminHttpError(400, "jobId is required");
|
|
7298
|
+
return runtime.adminRetryExternalMemory(namespace, jobId);
|
|
7299
|
+
}
|
|
7300
|
+
if (operation === "commit") {
|
|
7301
|
+
const jobId = typeof body.jobId === "string" ? body.jobId.trim() : "";
|
|
7302
|
+
if (!jobId) throw new AdminHttpError(400, "jobId is required");
|
|
7303
|
+
const allowed = /* @__PURE__ */ new Set(["ADD", "MERGE", "SUPERSEDE", "CONFLICT", "IGNORE"]);
|
|
7304
|
+
const choices = Array.isArray(body.choices) ? body.choices.flatMap((value) => {
|
|
7305
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return [];
|
|
7306
|
+
const item = value;
|
|
7307
|
+
const index = item.index;
|
|
7308
|
+
const action = typeof item.action === "string" ? item.action.toUpperCase() : "IGNORE";
|
|
7309
|
+
return Number.isSafeInteger(index) && allowed.has(action) ? [{ index, action }] : [];
|
|
7310
|
+
}) : [];
|
|
7311
|
+
return runtime.adminCommitExternalMemory(namespace, jobId, choices);
|
|
7312
|
+
}
|
|
7313
|
+
if (operation === "undo") {
|
|
7314
|
+
const sourceBlockId = typeof body.sourceBlockId === "string" ? body.sourceBlockId.trim() : "";
|
|
7315
|
+
if (!sourceBlockId) throw new AdminHttpError(400, "sourceBlockId is required");
|
|
7316
|
+
return runtime.adminUndoExternalMemory(namespace, sourceBlockId);
|
|
7317
|
+
}
|
|
7318
|
+
throw new AdminHttpError(400, "operation must be preview, status, retry, commit, or undo");
|
|
6661
7319
|
}
|
|
6662
7320
|
function externalMemoryPrompt() {
|
|
6663
7321
|
return { prompt: EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN, schemaVersion: "stratagate.external-memory.v2" };
|
|
@@ -7084,8 +7742,17 @@ async function handleAdminRequest(runtime, req, res) {
|
|
|
7084
7742
|
if (req.method !== "PATCH") throw new AdminHttpError(405, "StrataGate Block expansion requires PATCH");
|
|
7085
7743
|
sendJson(res, 200, await expandBlock(runtime, url));
|
|
7086
7744
|
} else if (path === "/api/stratagate/import") {
|
|
7087
|
-
if (req.method === "GET")
|
|
7088
|
-
|
|
7745
|
+
if (req.method === "GET") {
|
|
7746
|
+
const operation = url.searchParams.get("operation");
|
|
7747
|
+
if (operation === "status") {
|
|
7748
|
+
const namespace = url.searchParams.get("namespace")?.trim() ?? "";
|
|
7749
|
+
if (!namespace) throw new AdminHttpError(400, "namespace is required");
|
|
7750
|
+
const jobId = url.searchParams.get("jobId")?.trim() || void 0;
|
|
7751
|
+
sendJson(res, 200, await runtime.adminExternalMemoryStatus(namespace, jobId));
|
|
7752
|
+
} else {
|
|
7753
|
+
sendJson(res, 200, externalMemoryPrompt());
|
|
7754
|
+
}
|
|
7755
|
+
} else if (req.method === "POST") sendJson(res, 200, await importExternalMemory(runtime, req));
|
|
7089
7756
|
else throw new AdminHttpError(405, "External memory import requires GET or POST");
|
|
7090
7757
|
} else if (req.method !== "GET") throw new AdminHttpError(405, "StrataGate memory data is read-only");
|
|
7091
7758
|
else if (path === "/api/stratagate/dashboard") sendDashboard(res, await dashboard(runtime, url, requestHeader(req, "if-none-match")));
|