stratagate-dsh 0.2.82 → 0.2.84
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 +11 -0
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/client.js +7 -2
- package/dist/plugin.js +79 -21
- package/dist/plugin.js.map +1 -1
- package/docs/DSH.md +1 -1
- package/docs/DSH.zh-CN.md +1 -1
- package/docs/assets/short-term-memory-explainer-en.gif +0 -0
- package/docs/assets/short-term-memory-explainer-zh.gif +0 -0
- package/docs/assets/stratagate-profile-0.2.83-conflict.png +0 -0
- package/docs/assets/stratagate-profile-0.2.83-lower.png +0 -0
- package/docs/assets/stratagate-profile-0.2.83-reasoning-conflict.png +0 -0
- package/docs/assets/stratagate-profile-0.2.83.png +0 -0
- package/package.json +3 -1
package/dist/plugin.js
CHANGED
|
@@ -1614,13 +1614,15 @@ function normalizeRetrievalAssessment(input, batchEvidenceRefs) {
|
|
|
1614
1614
|
var PROFILE_FIELDS = {
|
|
1615
1615
|
userPreferredName: { label: "User preferred name", maxLength: 100 },
|
|
1616
1616
|
assistantPreferredName: { label: "Assistant preferred name", maxLength: 100 },
|
|
1617
|
-
preferredLanguage: { label: "Preferred language", maxLength: 100 },
|
|
1617
|
+
preferredLanguage: { label: "Preferred answer language", maxLength: 100 },
|
|
1618
|
+
reasoningLanguage: { label: "Preferred visible reasoning language", maxLength: 100 },
|
|
1618
1619
|
responsePreferences: { label: "Response preferences", maxLength: 1e3 },
|
|
1619
1620
|
standingInstructions: { label: "Standing instructions", maxLength: 1e3 },
|
|
1620
1621
|
userBackground: { label: "User background", maxLength: 1500 },
|
|
1621
1622
|
longTermGoals: { label: "Long-term goals", maxLength: 1e3 },
|
|
1622
1623
|
persistentNotes: { label: "Persistent notes", maxLength: 1200 }
|
|
1623
1624
|
};
|
|
1625
|
+
var PROFILE_PROTECTED_SHORT_FIELDS = ["userPreferredName", "assistantPreferredName", "preferredLanguage", "reasoningLanguage"];
|
|
1624
1626
|
var PROFILE_TOTAL_MAX_LENGTH = 6e3;
|
|
1625
1627
|
var PROFILE_MAINTENANCE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1626
1628
|
var PROFILE_MAINTENANCE_TOTAL_THRESHOLD = 4800;
|
|
@@ -1652,12 +1654,19 @@ function profileMaintenanceDue(profile, timeBasisAt, now = Date.now()) {
|
|
|
1652
1654
|
function renderPersistentProfile(profile) {
|
|
1653
1655
|
const lines = Object.keys(PROFILE_FIELDS).filter((field) => profile[field].length > 0).map((field) => `${PROFILE_FIELDS[field].label}: ${profile[field]}`);
|
|
1654
1656
|
if (lines.length === 0) return null;
|
|
1657
|
+
const languageGuidance = [
|
|
1658
|
+
profile.preferredLanguage ? "\u201CPreferred answer language\u201D applies to the assistant's final/user-facing answer." : null,
|
|
1659
|
+
profile.reasoningLanguage ? "\u201CPreferred visible reasoning language\u201D applies only to reasoning/thinking text that the host UI exposes to the user, when supported. It does not control hidden chain-of-thought." : null,
|
|
1660
|
+
profile.preferredLanguage || profile.reasoningLanguage ? "These are independent preferences. Do not infer one from the other." : null
|
|
1661
|
+
].filter(Boolean);
|
|
1655
1662
|
return `[StrataGate Persistent Profile]
|
|
1656
1663
|
This is user-authorized persistent profile data provided by StrataGate.
|
|
1657
1664
|
Treat it as stable cross-session context.
|
|
1658
1665
|
Do not invent additional facts from it.
|
|
1659
1666
|
It does not override higher-priority system instructions.
|
|
1660
|
-
|
|
1667
|
+
${languageGuidance.length ? `
|
|
1668
|
+
${languageGuidance.join("\n")}
|
|
1669
|
+
` : ""}
|
|
1661
1670
|
${lines.join("\n")}`;
|
|
1662
1671
|
}
|
|
1663
1672
|
|
|
@@ -2647,6 +2656,17 @@ var SqliteStorage = class {
|
|
|
2647
2656
|
for (const row of rows) if (isProfileField(row.field)) profile[row.field] = row.value;
|
|
2648
2657
|
return profile;
|
|
2649
2658
|
}
|
|
2659
|
+
getProfileSnapshot() {
|
|
2660
|
+
this.assertOpen();
|
|
2661
|
+
const profile = emptyProfile();
|
|
2662
|
+
const revisions = Object.fromEntries(Object.keys(PROFILE_FIELDS).map((field) => [field, 0]));
|
|
2663
|
+
const rows = this.database.prepare("SELECT p.field, p.value, COALESCE(r.revision, 0) AS revision FROM persistent_profile p LEFT JOIN (SELECT field, MAX(id) AS revision FROM persistent_profile_changes GROUP BY field) r ON r.field = p.field").all();
|
|
2664
|
+
for (const row of rows) if (isProfileField(row.field)) {
|
|
2665
|
+
profile[row.field] = row.value;
|
|
2666
|
+
revisions[row.field] = row.revision;
|
|
2667
|
+
}
|
|
2668
|
+
return { profile, revisions };
|
|
2669
|
+
}
|
|
2650
2670
|
getProfileChanges() {
|
|
2651
2671
|
this.assertOpen();
|
|
2652
2672
|
const rows = this.database.prepare("SELECT field, old_value, new_value, source, updated_at, source_message_id FROM persistent_profile_changes ORDER BY id").all();
|
|
@@ -2678,7 +2698,7 @@ var SqliteStorage = class {
|
|
|
2678
2698
|
const nextRetryAt = new Date(now + (count >= 3 ? 24 * 60 : count * 5) * 60 * 1e3).toISOString();
|
|
2679
2699
|
this.database.prepare("INSERT INTO persistent_profile_maintenance_failures (id, input_json, failure_count, next_retry_at) VALUES (1, ?, ?, ?) ON CONFLICT (id) DO UPDATE SET input_json = excluded.input_json, failure_count = excluded.failure_count, next_retry_at = excluded.next_retry_at").run(inputJson, count, nextRetryAt);
|
|
2680
2700
|
}
|
|
2681
|
-
updateProfileField(field, value, source, sourceMessageId) {
|
|
2701
|
+
updateProfileField(field, value, source, sourceMessageId, expectedValue, expectedRevision) {
|
|
2682
2702
|
this.assertOpen();
|
|
2683
2703
|
if (!isProfileField(field)) throw new TypeError(`Unknown Persistent Profile field: ${field}`);
|
|
2684
2704
|
if (typeof value !== "string") throw new TypeError("Persistent Profile value must be a string");
|
|
@@ -2687,6 +2707,11 @@ var SqliteStorage = class {
|
|
|
2687
2707
|
const profile = this.getPersistentProfile();
|
|
2688
2708
|
const wasNonempty = Object.values(profile).some((entry) => entry.length > 0);
|
|
2689
2709
|
const oldValue = profile[field];
|
|
2710
|
+
if (expectedRevision !== void 0) {
|
|
2711
|
+
const row = this.database.prepare("SELECT COALESCE(MAX(id), 0) AS revision FROM persistent_profile_changes WHERE field = ?").get(field);
|
|
2712
|
+
if (row.revision !== expectedRevision) return { field, value: oldValue, modified: false, conflict: true };
|
|
2713
|
+
}
|
|
2714
|
+
if (expectedValue !== void 0 && oldValue !== expectedValue) return { field, value: oldValue, modified: false, conflict: true };
|
|
2690
2715
|
if (oldValue === value) return { field, value, modified: false };
|
|
2691
2716
|
profile[field] = value;
|
|
2692
2717
|
validateProfile(profile);
|
|
@@ -2708,6 +2733,9 @@ var SqliteStorage = class {
|
|
|
2708
2733
|
this.assertOpen();
|
|
2709
2734
|
validateProfile(proposed);
|
|
2710
2735
|
if (Object.keys(proposed).length !== Object.keys(PROFILE_FIELDS).length || Object.keys(proposed).some((field) => !isProfileField(field))) throw new TypeError("Maintenance returned unknown or missing Profile fields");
|
|
2736
|
+
for (const field of PROFILE_PROTECTED_SHORT_FIELDS) {
|
|
2737
|
+
if (proposed[field].trim() !== expected[field].trim()) throw new Error(`Profile maintenance changed protected short field ${field}`);
|
|
2738
|
+
}
|
|
2711
2739
|
return this.immediateTransaction(() => {
|
|
2712
2740
|
const current = this.getPersistentProfile();
|
|
2713
2741
|
if (JSON.stringify(current) !== JSON.stringify(expected)) return false;
|
|
@@ -5727,12 +5755,26 @@ var STRUCTURED_FIELDS = {
|
|
|
5727
5755
|
var STRING_ARRAY = { type: "array", items: { type: "string" } };
|
|
5728
5756
|
var OPEN_OBJECT = { type: "object", additionalProperties: true };
|
|
5729
5757
|
var SUMMARIZER_PARAMETERS = {
|
|
5730
|
-
l0Title: { type: "string", required: true },
|
|
5731
|
-
l0Tags: { ...STRING_ARRAY, required: true },
|
|
5732
|
-
l1Summary: { type: "string", required: true },
|
|
5733
|
-
l2Keypoints: { ...STRING_ARRAY, required: true },
|
|
5734
|
-
shouldExtract: { type: "boolean", required: true }
|
|
5758
|
+
l0Title: { type: "string", description: "L0: a short phrase naming the Block's central subject for quick recognition; do not turn it into a sentence-length summary.", required: true },
|
|
5759
|
+
l0Tags: { ...STRING_ARRAY, description: "L0: a few distinctive topical labels (usually 2-5) for rapid recognition of the Block, such as named people, projects, organizations, tools, technical topics, or specific task concepts; omit generic labels such as discussion, conversation, problem, or task.", required: true },
|
|
5760
|
+
l1Summary: { type: "string", description: "L1: a compact, self-contained overview of what happened across the Block, its conclusions or results, significant state changes, and key unresolved work; richer than L0 but still quick to read.", required: true },
|
|
5761
|
+
l2Keypoints: { ...STRING_ARRAY, description: "L2: specific, self-contained points needed to recover context later. Make each point mostly atomic: one decision, constraint, preference, fact, result, failure reason, or open item. Add detail beyond L1 without splitting or repeating its sentences.", required: true },
|
|
5762
|
+
shouldExtract: { type: "boolean", description: "High-recall pre-screen for Event extraction, not the final Event decision. True when this Block clearly contains or may reasonably contain a long-term Event; when uncertain, use true so the Event Extractor can decide. False only when it clearly lacks lasting value, such as greetings, repeated confirmations, or disposable process noise. Mere presence of a fact is not sufficient.", required: true }
|
|
5735
5763
|
};
|
|
5764
|
+
var SUMMARIZER_SYSTEM_PROMPT = `You are the Block Summarizer in StrataGate's memory pipeline. Compress one sealed conversation Block, typically about six turns, from the supplied provenance-preserving derivation messages into L0-L2 layered memory and decide whether it warrants later Event extraction. You are a background model, not the main Agent; you have no other conversation context. L5 retains the complete original source, while L3/L4 are produced separately. Your L0-L2 may later replace detailed chat in the Agent's context, so preserve what future work needs without inventing missing context.
|
|
5765
|
+
|
|
5766
|
+
L0 -> L1 -> L2 are progressively higher-resolution views of the same history, not three independent or repetitive summaries:
|
|
5767
|
+
- L0: l0Title is a short subject phrase for rapid recognition, ideally under about 60 characters; l0Tags are a few distinctive topical labels for rapid recognition, usually 2-5. Prefer named people, projects, organizations, tools, technical topics, and specific task concepts. Avoid generic tags such as discussion, conversation, problem, or task.
|
|
5768
|
+
- L1: l1Summary is a compact, self-contained overview of the whole Block: what happened, conclusions or results, meaningful state changes, and key unresolved work. Usually 1-3 sentences; do not retell the conversation turn by turn.
|
|
5769
|
+
- L2: l2Keypoints retain concrete details needed to resume work. Usually 3-8 concise points when there is enough substance, fewer for a thin Block. Each point should stand alone and express mainly one decision, constraint, preference, fact, result, significant failure reason, or open item. Be more specific than L1; do not merely split L1 into repeated sentences.
|
|
5770
|
+
|
|
5771
|
+
Prioritize important decisions, constraints, user preferences, final outcomes, significant failure causes, and unresolved work; then task-relevant process details. Omit repetition, greetings, and disposable execution noise. Do not discard a constraint or conclusion that would change how later work is understood just to shorten the output.
|
|
5772
|
+
|
|
5773
|
+
Keep provenance and uncertainty. Distinguish what the user stated or decided, what the assistant only proposed or suspected, and what a tool actually observed. Do not promote an assistant hypothesis or a recollection of older memory into a verified fact or new outcome. Preserve uncertainty about timing, causes, status, results, and relationships. Ordinary user/assistant text is retained, but large tool arguments or results may be compacted; use only retained tool names, evidence summaries, and excerpts, and never guess omitted payload details.
|
|
5774
|
+
|
|
5775
|
+
shouldExtract is a high-recall pre-screen, not the final Event decision. If it is false, Event extraction is skipped entirely; if true, the Event Extractor makes the final evidence-based decision. Set true when this Block clearly contains or may reasonably contain a long-term Event, such as a decision, stable preference, material project change, meaningful task result, important failure and possible cause, future-useful fact, or open item worth tracking. When uncertain whether a plausible candidate has lasting value, choose true and let the Event Extractor decide. Set false only when the Block clearly lacks such value, for example greetings, repeated confirmations, or disposable execution noise. Do not set true merely because some fact appears; an unsupported assistant suggestion or recap of older memory alone does not establish a new Event candidate.
|
|
5776
|
+
|
|
5777
|
+
Call stratagate_summarize_block exactly once with l0Title, l0Tags, l1Summary, l2Keypoints, and shouldExtract. Do not return the summary as ordinary text.`;
|
|
5736
5778
|
var EVENT_ITEM = {
|
|
5737
5779
|
type: "object",
|
|
5738
5780
|
additionalProperties: false,
|
|
@@ -5862,7 +5904,7 @@ var PROFILE_MAINTENANCE_PARAMETERS = Object.fromEntries(
|
|
|
5862
5904
|
var STRUCTURED_TOOLS = {
|
|
5863
5905
|
summarizer: {
|
|
5864
5906
|
name: "stratagate_summarize_block",
|
|
5865
|
-
description: "Submit the
|
|
5907
|
+
description: "Submit the L0-L2 layered compression of one sealed StrataGate conversation Block and decide whether it should proceed to Event extraction.",
|
|
5866
5908
|
parameters: SUMMARIZER_PARAMETERS
|
|
5867
5909
|
},
|
|
5868
5910
|
extractor: {
|
|
@@ -5892,7 +5934,7 @@ var STRUCTURED_TOOLS = {
|
|
|
5892
5934
|
},
|
|
5893
5935
|
profileMaintenance: {
|
|
5894
5936
|
name: "stratagate_maintain_profile",
|
|
5895
|
-
description: "Return the same
|
|
5937
|
+
description: "Return the same nine Persistent Profile fields with only safe wording and redundancy cleanup; preserve the four short fields apart from necessary whitespace cleanup.",
|
|
5896
5938
|
parameters: PROFILE_MAINTENANCE_PARAMETERS
|
|
5897
5939
|
}
|
|
5898
5940
|
};
|
|
@@ -6001,7 +6043,7 @@ var DshModelBridge = class {
|
|
|
6001
6043
|
summarizer = async (messages) => {
|
|
6002
6044
|
const raw = object(await this.callStructured(
|
|
6003
6045
|
"summarizer",
|
|
6004
|
-
|
|
6046
|
+
SUMMARIZER_SYSTEM_PROMPT,
|
|
6005
6047
|
{ messages: buildMemoryDerivationMessages(messages) }
|
|
6006
6048
|
));
|
|
6007
6049
|
return {
|
|
@@ -6189,7 +6231,7 @@ var DshModelBridge = class {
|
|
|
6189
6231
|
async maintainProfile(profile) {
|
|
6190
6232
|
const raw = object(await this.callStructured(
|
|
6191
6233
|
"profileMaintenance",
|
|
6192
|
-
`You maintain only the supplied StrataGate Persistent Profile. Call ${STRUCTURED_TOOLS.profileMaintenance.name} exactly once with all
|
|
6234
|
+
`You maintain only the supplied StrataGate Persistent Profile. Call ${STRUCTURED_TOOLS.profileMaintenance.name} exactly once with all nine string fields. You may deduplicate, merge repeated meaning, shorten redundant wording, and improve organization. Preserve every unique fact, uncertainty, constraint, and instruction. Never infer or add facts, broaden meaning, or read Event, Graph, or conversation history. If two statements might conflict or cannot safely merge, retain both. Keep userPreferredName, assistantPreferredName, preferredLanguage, and reasoningLanguage unchanged except necessary whitespace cleanup. preferredLanguage and reasoningLanguage are independent: never infer, copy, or merge either language field into the other. reasoningLanguage is only for user-visible reasoning/thinking text when supported, not hidden chain-of-thought. Character limits (Unicode code points): ${JSON.stringify(Object.fromEntries(Object.entries(PROFILE_FIELDS).map(([field, spec]) => [field, spec.maxLength])))}. Total maximum: 6000. If safe compression is impossible, return the original value.`,
|
|
6193
6235
|
{ profile, fieldDefinitions: PROFILE_FIELDS }
|
|
6194
6236
|
));
|
|
6195
6237
|
if (Object.keys(raw).length !== Object.keys(PROFILE_FIELDS).length || Object.keys(raw).some((field) => !(field in PROFILE_FIELDS))) {
|
|
@@ -6197,7 +6239,7 @@ var DshModelBridge = class {
|
|
|
6197
6239
|
}
|
|
6198
6240
|
const proposed = raw;
|
|
6199
6241
|
validateProfile(proposed);
|
|
6200
|
-
for (const field of
|
|
6242
|
+
for (const field of PROFILE_PROTECTED_SHORT_FIELDS) {
|
|
6201
6243
|
if (proposed[field].trim() !== profile[field].trim()) throw new Error(`Profile maintenance changed protected short field ${field}`);
|
|
6202
6244
|
}
|
|
6203
6245
|
return proposed;
|
|
@@ -6985,14 +7027,17 @@ var StrataGateRuntime = class {
|
|
|
6985
7027
|
getPersistentProfile() {
|
|
6986
7028
|
return this.profileStore().getPersistentProfile();
|
|
6987
7029
|
}
|
|
7030
|
+
getProfileSnapshot() {
|
|
7031
|
+
return this.profileStore().getProfileSnapshot();
|
|
7032
|
+
}
|
|
6988
7033
|
renderProfileContext() {
|
|
6989
7034
|
return renderPersistentProfile(this.getPersistentProfile());
|
|
6990
7035
|
}
|
|
6991
7036
|
getProfileChanges() {
|
|
6992
7037
|
return this.profileStore().getProfileChanges();
|
|
6993
7038
|
}
|
|
6994
|
-
updatePersistentProfile(field, value, source, sourceMessageId) {
|
|
6995
|
-
const result = this.profileStore().updateProfileField(field, value, source, sourceMessageId);
|
|
7039
|
+
updatePersistentProfile(field, value, source, sourceMessageId, expectedValue, expectedRevision) {
|
|
7040
|
+
const result = this.profileStore().updateProfileField(field, value, source, sourceMessageId, expectedValue, expectedRevision);
|
|
6996
7041
|
if (result.modified) this.wakeBackgroundWorker();
|
|
6997
7042
|
return result;
|
|
6998
7043
|
}
|
|
@@ -8811,7 +8856,14 @@ function sessionOf(exec) {
|
|
|
8811
8856
|
function registerMemoryTools(ctx, runtime) {
|
|
8812
8857
|
ctx.tools.register(defineTool({
|
|
8813
8858
|
name: "memory_profile_update",
|
|
8814
|
-
description: `This tool is provided by the StrataGate plugin. Update exactly one field of the user's global Persistent Profile. Persistent Profile data is injected into every future conversation without retrieval, so use this tool only for information that should remain continuously available or continuously affect future behavior, such as how to address the user, what the user wants the assistant to be called,
|
|
8859
|
+
description: `This tool is provided by the StrataGate plugin. Update exactly one field of the user's global Persistent Profile. Persistent Profile data is injected into every future conversation without retrieval, so use this tool only for information that should remain continuously available or continuously affect future behavior, such as how to address the user, what the user wants the assistant to be called, language preferences, stable response preferences, standing instructions, stable user background, long-term goals, or other genuinely persistent notes.
|
|
8860
|
+
|
|
8861
|
+
preferredLanguage sets only the default language of the final/user-facing answer. reasoningLanguage sets only the desired language of reasoning/thinking text visible to the user in the DSH or host UI, when supported; it cannot control hidden internal chain-of-thought. An empty reasoningLanguage adds no visible-reasoning language requirement. These are independent settings: never infer one from the other, and never change one merely because the other changed.
|
|
8862
|
+
|
|
8863
|
+
Examples:
|
|
8864
|
+
User: \u201C\u4EE5\u540E\u90FD\u7528\u4E2D\u6587\u56DE\u7B54\u6211\u201D \u2192 call memory_profile_update with preferredLanguage = \u4E2D\u6587; do not change reasoningLanguage.
|
|
8865
|
+
User: \u201C\u4EE5\u540E\u601D\u8003\u8FC7\u7A0B\u7528\u4E2D\u6587\u201D or \u201C\u601D\u8003\u94FE\u7528\u4E2D\u6587\u201D \u2192 call memory_profile_update with reasoningLanguage = \u4E2D\u6587; do not change preferredLanguage.
|
|
8866
|
+
User: \u201C\u4EE5\u540E\u56DE\u7B54\u548C\u601D\u8003\u8FC7\u7A0B\u90FD\u7528\u4E2D\u6587\u201D \u2192 make two separate memory_profile_update calls: first preferredLanguage = \u4E2D\u6587, then reasoningLanguage = \u4E2D\u6587. Each call still changes exactly one field.
|
|
8815
8867
|
|
|
8816
8868
|
Do not use this tool merely because the user says "remember". If the information describes something that happened, a decision, an activity, a project change, a dated fact, or something that only needs to be recalled when relevant, it belongs in Event memory instead. A separate Event-memory tool is reserved for that purpose and is not part of this implementation.
|
|
8817
8869
|
|
|
@@ -8821,7 +8873,7 @@ If the assistant only infers that something might be a useful persistent prefere
|
|
|
8821
8873
|
|
|
8822
8874
|
Each call changes exactly one predefined Profile field. Never create, delete, or rename Profile fields, and never rewrite the complete Profile when only one field is being changed.`,
|
|
8823
8875
|
parameters: {
|
|
8824
|
-
field: { type: "string", required: true, enum: ["userPreferredName", "assistantPreferredName", "preferredLanguage", "responsePreferences", "standingInstructions", "userBackground", "longTermGoals", "persistentNotes"] },
|
|
8876
|
+
field: { type: "string", required: true, enum: ["userPreferredName", "assistantPreferredName", "preferredLanguage", "reasoningLanguage", "responsePreferences", "standingInstructions", "userBackground", "longTermGoals", "persistentNotes"] },
|
|
8825
8877
|
value: { type: "string", required: true }
|
|
8826
8878
|
},
|
|
8827
8879
|
output: jsonOutput,
|
|
@@ -9579,7 +9631,10 @@ async function updateSettings(runtime, url) {
|
|
|
9579
9631
|
return result;
|
|
9580
9632
|
}
|
|
9581
9633
|
async function persistentProfile(runtime, req) {
|
|
9582
|
-
if (req.method === "GET")
|
|
9634
|
+
if (req.method === "GET") {
|
|
9635
|
+
const snapshot = runtime.getProfileSnapshot();
|
|
9636
|
+
return { ...snapshot.profile, _revisions: snapshot.revisions };
|
|
9637
|
+
}
|
|
9583
9638
|
if (req.method !== "PATCH") throw new AdminHttpError(405, "Persistent Profile requires GET or PATCH");
|
|
9584
9639
|
let suppliedBody = req.body;
|
|
9585
9640
|
if (suppliedBody === void 0 && typeof req[Symbol.asyncIterator] === "function") {
|
|
@@ -9603,11 +9658,14 @@ async function persistentProfile(runtime, req) {
|
|
|
9603
9658
|
}
|
|
9604
9659
|
if (!body || typeof body !== "object" || Array.isArray(body)) throw new AdminHttpError(400, "Profile update must be an object");
|
|
9605
9660
|
const input = body;
|
|
9606
|
-
if (Object.keys(input).length !==
|
|
9607
|
-
throw new AdminHttpError(400, "Profile update requires
|
|
9661
|
+
if (Object.keys(input).length !== 4 || !Object.hasOwn(input, "field") || !Object.hasOwn(input, "value") || !Object.hasOwn(input, "expectedValue") || !Object.hasOwn(input, "expectedRevision") || typeof input.field !== "string" || typeof input.value !== "string" || typeof input.expectedValue !== "string" || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0) {
|
|
9662
|
+
throw new AdminHttpError(400, "Profile update requires field, value, expectedValue strings and expectedRevision number");
|
|
9608
9663
|
}
|
|
9609
9664
|
try {
|
|
9610
|
-
|
|
9665
|
+
const result = runtime.updatePersistentProfile(input.field, input.value, "settings", null, input.expectedValue, input.expectedRevision);
|
|
9666
|
+
if (result.conflict) throw new AdminHttpError(409, "\u8BE5\u9879\u521A\u521A\u5728\u5176\u4ED6\u4F4D\u7F6E\u66F4\u65B0");
|
|
9667
|
+
const snapshot = runtime.getProfileSnapshot();
|
|
9668
|
+
return { ...result, snapshot: { ...snapshot.profile, _revisions: snapshot.revisions } };
|
|
9611
9669
|
} catch (error) {
|
|
9612
9670
|
if (error instanceof TypeError || error instanceof RangeError) throw new AdminHttpError(400, error.message);
|
|
9613
9671
|
throw error;
|