dsh-daoing-memory 0.1.9 → 0.1.10
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/lib/index.js +1324 -12
- package/lib/types/core.d.ts +32 -1
- package/lib/types/core.js +123 -3
- package/lib/types/service.d.ts +17 -1
- package/lib/types/service.js +141 -3
- package/lib/types/store.d.ts +14 -2
- package/lib/types/store.js +88 -1
- package/lib/types/types.d.ts +57 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { join, resolve } from "node:path";
|
|
3
4
|
import { DatabaseSync } from "node:sqlite";
|
|
4
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
+
import "@deepseek-ai/cordis";
|
|
5
7
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
6
8
|
//#region lib/types/store.js
|
|
7
9
|
/**
|
|
@@ -12,7 +14,7 @@ import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
|
12
14
|
* @module dsh-daoing-memory/store
|
|
13
15
|
*/
|
|
14
16
|
/** Monotone store schema version. v1→v2 is an additive ALTER migration (see open()). */
|
|
15
|
-
const MEMORY_SCHEMA_VERSION =
|
|
17
|
+
const MEMORY_SCHEMA_VERSION = 6;
|
|
16
18
|
/** Half-life (ms) of the recency weighting applied to verification samples. */
|
|
17
19
|
const TRUST_HALF_LIFE_MS = 4320 * 60 * 60 * 1e3;
|
|
18
20
|
/** Hash one ledger block's content, chained to the previous hash. */
|
|
@@ -182,7 +184,7 @@ var MemoryStore = class {
|
|
|
182
184
|
);
|
|
183
185
|
`);
|
|
184
186
|
const row = db.prepare("SELECT value FROM memory_meta WHERE key = ?").get("schema_version");
|
|
185
|
-
if (row === void 0) db.prepare("INSERT INTO memory_meta (key, value) VALUES (?, ?)").run("schema_version", String(
|
|
187
|
+
if (row === void 0) db.prepare("INSERT INTO memory_meta (key, value) VALUES (?, ?)").run("schema_version", String(6));
|
|
186
188
|
else {
|
|
187
189
|
let v = Number(row.value);
|
|
188
190
|
if (v === 1) {
|
|
@@ -201,8 +203,30 @@ var MemoryStore = class {
|
|
|
201
203
|
db.exec(`ALTER TABLE concerns ADD COLUMN background TEXT NOT NULL DEFAULT '';`);
|
|
202
204
|
v = 5;
|
|
203
205
|
}
|
|
204
|
-
if (v
|
|
205
|
-
|
|
206
|
+
if (v === 5 && true) {
|
|
207
|
+
db.exec(`
|
|
208
|
+
CREATE TABLE IF NOT EXISTS skill_artifacts (
|
|
209
|
+
id TEXT PRIMARY KEY,
|
|
210
|
+
parent_experience_id TEXT NOT NULL,
|
|
211
|
+
form TEXT NOT NULL,
|
|
212
|
+
status TEXT NOT NULL,
|
|
213
|
+
draft_path TEXT,
|
|
214
|
+
published_path TEXT,
|
|
215
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
216
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
217
|
+
optimize_count INTEGER NOT NULL DEFAULT 0,
|
|
218
|
+
last_feedback TEXT,
|
|
219
|
+
content_hash TEXT,
|
|
220
|
+
created_at INTEGER NOT NULL,
|
|
221
|
+
updated_at INTEGER NOT NULL
|
|
222
|
+
);
|
|
223
|
+
CREATE INDEX IF NOT EXISTS idx_skill_parent ON skill_artifacts (parent_experience_id);
|
|
224
|
+
CREATE INDEX IF NOT EXISTS idx_skill_status ON skill_artifacts (status);
|
|
225
|
+
`);
|
|
226
|
+
v = 6;
|
|
227
|
+
}
|
|
228
|
+
if (v !== 6) throw new Error(`memory store schema version ${row.value} does not match 6; refusing to open`);
|
|
229
|
+
db.prepare("UPDATE memory_meta SET value = ? WHERE key = ?").run(String(6), "schema_version");
|
|
206
230
|
}
|
|
207
231
|
db.exec("CREATE INDEX IF NOT EXISTS idx_exp_context ON experiences (context);");
|
|
208
232
|
db.exec("CREATE INDEX IF NOT EXISTS idx_concern_parent ON concerns (parent_id);");
|
|
@@ -857,6 +881,60 @@ var MemoryStore = class {
|
|
|
857
881
|
if (row.superseded_by !== null) fact.supersededBy = row.superseded_by;
|
|
858
882
|
return fact;
|
|
859
883
|
}
|
|
884
|
+
/** Insert or update a skill artifact. */
|
|
885
|
+
upsertSkillArtifact(artifact) {
|
|
886
|
+
this.db.prepare(`
|
|
887
|
+
INSERT INTO skill_artifacts (id, parent_experience_id, form, status, draft_path, published_path,
|
|
888
|
+
version, use_count, optimize_count, last_feedback, content_hash, created_at, updated_at)
|
|
889
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
890
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
891
|
+
status=excluded.status, draft_path=excluded.draft_path, published_path=excluded.published_path,
|
|
892
|
+
version=excluded.version, use_count=excluded.use_count, optimize_count=excluded.optimize_count,
|
|
893
|
+
last_feedback=excluded.last_feedback, content_hash=excluded.content_hash, updated_at=excluded.updated_at
|
|
894
|
+
`).run(artifact.id, artifact.parentExperienceId, artifact.form, artifact.status, artifact.draftPath ?? null, artifact.publishedPath ?? null, artifact.version, artifact.useCount, artifact.optimizeCount, artifact.lastFeedback ?? null, artifact.contentHash ?? null, artifact.createdAt, artifact.updatedAt);
|
|
895
|
+
}
|
|
896
|
+
/** Get a skill artifact by id. */
|
|
897
|
+
getSkillArtifact(id) {
|
|
898
|
+
const row = this.db.prepare("SELECT * FROM skill_artifacts WHERE id = ?").get(id);
|
|
899
|
+
return row ? this.rowToSkillArtifact(row) : void 0;
|
|
900
|
+
}
|
|
901
|
+
/** List skill artifacts, optionally filtered by parent experience or status. */
|
|
902
|
+
listSkillArtifacts(filter) {
|
|
903
|
+
const clauses = ["1 = 1"];
|
|
904
|
+
const params = [];
|
|
905
|
+
if (filter?.parentExperienceId !== void 0) {
|
|
906
|
+
clauses.push("parent_experience_id = ?");
|
|
907
|
+
params.push(filter.parentExperienceId);
|
|
908
|
+
}
|
|
909
|
+
if (filter?.status !== void 0) {
|
|
910
|
+
clauses.push("status = ?");
|
|
911
|
+
params.push(filter.status);
|
|
912
|
+
}
|
|
913
|
+
return this.db.prepare(`SELECT * FROM skill_artifacts WHERE ${clauses.join(" AND ")} ORDER BY created_at DESC`).all(...params).map((r) => this.rowToSkillArtifact(r));
|
|
914
|
+
}
|
|
915
|
+
/** Count skill artifacts by status. */
|
|
916
|
+
countSkillArtifacts(status) {
|
|
917
|
+
if (status === void 0) return this.db.prepare("SELECT COUNT(*) AS n FROM skill_artifacts").get().n;
|
|
918
|
+
return this.db.prepare("SELECT COUNT(*) AS n FROM skill_artifacts WHERE status = ?").get(status).n;
|
|
919
|
+
}
|
|
920
|
+
rowToSkillArtifact(row) {
|
|
921
|
+
const artifact = {
|
|
922
|
+
id: row.id,
|
|
923
|
+
parentExperienceId: row.parent_experience_id,
|
|
924
|
+
form: row.form,
|
|
925
|
+
status: row.status,
|
|
926
|
+
version: row.version,
|
|
927
|
+
useCount: row.use_count,
|
|
928
|
+
optimizeCount: row.optimize_count,
|
|
929
|
+
createdAt: row.created_at,
|
|
930
|
+
updatedAt: row.updated_at
|
|
931
|
+
};
|
|
932
|
+
if (row.draft_path !== null) artifact.draftPath = row.draft_path;
|
|
933
|
+
if (row.published_path !== null) artifact.publishedPath = row.published_path;
|
|
934
|
+
if (row.last_feedback !== null) artifact.lastFeedback = row.last_feedback;
|
|
935
|
+
if (row.content_hash !== null) artifact.contentHash = row.content_hash;
|
|
936
|
+
return artifact;
|
|
937
|
+
}
|
|
860
938
|
};
|
|
861
939
|
//#endregion
|
|
862
940
|
//#region lib/types/types.js
|
|
@@ -1967,10 +2045,8 @@ var MemoryCore = class MemoryCore {
|
|
|
1967
2045
|
}, "deletion-feedback summarization");
|
|
1968
2046
|
return next;
|
|
1969
2047
|
}
|
|
1970
|
-
const id = crypto.randomUUID();
|
|
1971
2048
|
const exp = {
|
|
1972
|
-
id,
|
|
1973
|
-
familyId: id,
|
|
2049
|
+
id: crypto.randomUUID(),
|
|
1974
2050
|
revision: 1,
|
|
1975
2051
|
kind: "positive",
|
|
1976
2052
|
source: "system",
|
|
@@ -1989,6 +2065,9 @@ var MemoryCore = class MemoryCore {
|
|
|
1989
2065
|
status: "live",
|
|
1990
2066
|
alpha: 5,
|
|
1991
2067
|
beta: 2,
|
|
2068
|
+
samples: 7,
|
|
2069
|
+
trust: 6 / 9,
|
|
2070
|
+
weightedTrust: 6 / 9,
|
|
1992
2071
|
pinned: false,
|
|
1993
2072
|
tokensSaved: 0,
|
|
1994
2073
|
tokensSpent: 0,
|
|
@@ -2014,6 +2093,110 @@ var MemoryCore = class MemoryCore {
|
|
|
2014
2093
|
getDeletionFeedback() {
|
|
2015
2094
|
return this.store.findExperienceByFamily(MemoryCore.DELETION_FEEDBACK_FAMILY);
|
|
2016
2095
|
}
|
|
2096
|
+
/**
|
|
2097
|
+
* Create a skill artifact draft from LLM-generated content.
|
|
2098
|
+
* @param experienceId - parent experience family_id.
|
|
2099
|
+
* @param form - output form (skill_md or script_mjs).
|
|
2100
|
+
* @param content - the LLM-generated skill/script content.
|
|
2101
|
+
* @param draftPath - file path where the draft is saved.
|
|
2102
|
+
* @param actor - who triggered the generation.
|
|
2103
|
+
* @returns the created skill artifact.
|
|
2104
|
+
*/
|
|
2105
|
+
createSkillDraft(experienceId, form, content, draftPath, actor) {
|
|
2106
|
+
const experience = this.store.getActiveRevision(experienceId);
|
|
2107
|
+
if (experience === void 0) throw new Error(`memory: experience not found: ${experienceId}`);
|
|
2108
|
+
const now = Date.now();
|
|
2109
|
+
const id = crypto.randomUUID();
|
|
2110
|
+
const artifact = {
|
|
2111
|
+
id,
|
|
2112
|
+
parentExperienceId: experienceId,
|
|
2113
|
+
form,
|
|
2114
|
+
status: "draft",
|
|
2115
|
+
draftPath,
|
|
2116
|
+
version: 1,
|
|
2117
|
+
useCount: 0,
|
|
2118
|
+
optimizeCount: 0,
|
|
2119
|
+
contentHash: createHash("sha256").update(content).digest("hex"),
|
|
2120
|
+
createdAt: now,
|
|
2121
|
+
updatedAt: now
|
|
2122
|
+
};
|
|
2123
|
+
this.store.upsertSkillArtifact(artifact);
|
|
2124
|
+
this.ledger("skill-draft", "experience", experienceId, actor, {
|
|
2125
|
+
skillId: id,
|
|
2126
|
+
form,
|
|
2127
|
+
gist: experience.gist.slice(0, 80)
|
|
2128
|
+
}, `skill draft generated from experience`);
|
|
2129
|
+
return artifact;
|
|
2130
|
+
}
|
|
2131
|
+
/**
|
|
2132
|
+
* Review a skill artifact: approve or reject.
|
|
2133
|
+
*/
|
|
2134
|
+
reviewSkill(request, actor) {
|
|
2135
|
+
const artifact = this.store.getSkillArtifact(request.id);
|
|
2136
|
+
if (artifact === void 0) throw new Error(`memory: skill artifact not found: ${request.id}`);
|
|
2137
|
+
if (artifact.status !== "draft" && artifact.status !== "pending_review" && artifact.status !== "revising") throw new Error(`memory: skill artifact ${request.id} is in status "${artifact.status}", cannot review`);
|
|
2138
|
+
const now = Date.now();
|
|
2139
|
+
const updated = {
|
|
2140
|
+
...artifact,
|
|
2141
|
+
status: request.decision === "approve" ? "approved" : "rejected",
|
|
2142
|
+
lastFeedback: JSON.stringify({
|
|
2143
|
+
decision: request.decision,
|
|
2144
|
+
reason: request.reason,
|
|
2145
|
+
ts: now
|
|
2146
|
+
}),
|
|
2147
|
+
updatedAt: now
|
|
2148
|
+
};
|
|
2149
|
+
this.store.upsertSkillArtifact(updated);
|
|
2150
|
+
this.ledger(request.decision === "approve" ? "skill-approve" : "skill-reject", "experience", artifact.parentExperienceId, actor, {
|
|
2151
|
+
skillId: request.id,
|
|
2152
|
+
form: artifact.form,
|
|
2153
|
+
version: artifact.version
|
|
2154
|
+
}, request.reason);
|
|
2155
|
+
return updated;
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Publish a skill artifact: copy draft to $DSH_HOME/skills/ and mark as published.
|
|
2159
|
+
* The actual file copy is done by the service layer; this method updates the DB record.
|
|
2160
|
+
*/
|
|
2161
|
+
publishSkill(request, publishedPath, actor) {
|
|
2162
|
+
const artifact = this.store.getSkillArtifact(request.id);
|
|
2163
|
+
if (artifact === void 0) throw new Error(`memory: skill artifact not found: ${request.id}`);
|
|
2164
|
+
if (artifact.status !== "approved") throw new Error(`memory: skill artifact ${request.id} must be approved before publishing (current: ${artifact.status})`);
|
|
2165
|
+
const now = Date.now();
|
|
2166
|
+
const updated = {
|
|
2167
|
+
...artifact,
|
|
2168
|
+
status: "published",
|
|
2169
|
+
publishedPath,
|
|
2170
|
+
updatedAt: now
|
|
2171
|
+
};
|
|
2172
|
+
this.store.upsertSkillArtifact(updated);
|
|
2173
|
+
this.ledger("skill-publish", "experience", artifact.parentExperienceId, actor, {
|
|
2174
|
+
skillId: request.id,
|
|
2175
|
+
form: artifact.form,
|
|
2176
|
+
publishedPath
|
|
2177
|
+
}, request.reason);
|
|
2178
|
+
return updated;
|
|
2179
|
+
}
|
|
2180
|
+
/** List skill artifacts, optionally filtered. */
|
|
2181
|
+
listSkillArtifacts(filter) {
|
|
2182
|
+
return this.store.listSkillArtifacts(filter);
|
|
2183
|
+
}
|
|
2184
|
+
/** Get a single skill artifact. */
|
|
2185
|
+
getSkillArtifact(id) {
|
|
2186
|
+
return this.store.getSkillArtifact(id);
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* Check if an experience is a candidate for skill conversion.
|
|
2190
|
+
* Criteria: live status, enough recall events, complex path (≥3 steps).
|
|
2191
|
+
*/
|
|
2192
|
+
isSkillCandidate(experienceId) {
|
|
2193
|
+
const exp = this.store.getActiveRevision(experienceId);
|
|
2194
|
+
if (exp === void 0 || exp.status !== "live") return false;
|
|
2195
|
+
if (exp.path.length < 3) return false;
|
|
2196
|
+
if (exp.verifiedCount < 2) return false;
|
|
2197
|
+
if (this.store.listSkillArtifacts({ parentExperienceId: experienceId }).some((a) => a.status === "published" || a.status === "approved" || a.status === "pending_review")) return false;
|
|
2198
|
+
return true;
|
|
2199
|
+
}
|
|
2017
2200
|
/** Human pin/unpin: pinned cards keep the trust floor and escape budgets. */
|
|
2018
2201
|
humanPin(request, actor) {
|
|
2019
2202
|
const current = this.store.getActiveRevision(request.id);
|
|
@@ -2403,6 +2586,955 @@ var MemoryCore = class MemoryCore {
|
|
|
2403
2586
|
}
|
|
2404
2587
|
};
|
|
2405
2588
|
//#endregion
|
|
2589
|
+
//#region ../../llm/llm/src/brand.ts
|
|
2590
|
+
/**
|
|
2591
|
+
* Brand a message identifier.
|
|
2592
|
+
* @param id - the opaque message identifier.
|
|
2593
|
+
* @returns the same string, branded; no validation is performed.
|
|
2594
|
+
*/
|
|
2595
|
+
function MessageId(id) {
|
|
2596
|
+
return id;
|
|
2597
|
+
}
|
|
2598
|
+
//#endregion
|
|
2599
|
+
//#region ../../llm/llm/src/call-config.ts
|
|
2600
|
+
/**
|
|
2601
|
+
* Deep-freeze a value in place with an iterative traversal, guarding cycles,
|
|
2602
|
+
* so later mutation throws without imposing a JavaScript call-stack depth cap.
|
|
2603
|
+
* {@link AbortSignal} objects are deliberately skipped because they are the
|
|
2604
|
+
* request's live cancellation channel and freezing them breaks abort.
|
|
2605
|
+
* @param value - the value to freeze in place.
|
|
2606
|
+
* @returns the same value, frozen.
|
|
2607
|
+
*/
|
|
2608
|
+
function deepFreeze(value) {
|
|
2609
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
2610
|
+
const pending = [{
|
|
2611
|
+
kind: "visit",
|
|
2612
|
+
node: value
|
|
2613
|
+
}];
|
|
2614
|
+
while (pending.length > 0) {
|
|
2615
|
+
const task = pending.pop();
|
|
2616
|
+
/* v8 ignore next -- the loop condition guarantees one pending task. */
|
|
2617
|
+
if (task === void 0) continue;
|
|
2618
|
+
if (task.kind === "property") {
|
|
2619
|
+
pending.push({
|
|
2620
|
+
kind: "visit",
|
|
2621
|
+
node: task.source[task.key]
|
|
2622
|
+
});
|
|
2623
|
+
continue;
|
|
2624
|
+
}
|
|
2625
|
+
const node = task.node;
|
|
2626
|
+
if (node === null || typeof node !== "object") continue;
|
|
2627
|
+
if (node instanceof AbortSignal) continue;
|
|
2628
|
+
if (seen.has(node)) continue;
|
|
2629
|
+
seen.add(node);
|
|
2630
|
+
Object.freeze(node);
|
|
2631
|
+
const keys = Object.keys(node);
|
|
2632
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
2633
|
+
const key = keys[index];
|
|
2634
|
+
/* v8 ignore next -- the loop is bounded by the captured key count. */
|
|
2635
|
+
if (key === void 0) continue;
|
|
2636
|
+
pending.push({
|
|
2637
|
+
kind: "property",
|
|
2638
|
+
source: node,
|
|
2639
|
+
key
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
return value;
|
|
2644
|
+
}
|
|
2645
|
+
//#endregion
|
|
2646
|
+
//#region ../../llm/llm/src/message.ts
|
|
2647
|
+
/** Message value types, identity, and immutable construction helpers. */
|
|
2648
|
+
/**
|
|
2649
|
+
* Detach and deep-freeze a message whose identity already exists.
|
|
2650
|
+
* @param message - complete message, including its stable identity.
|
|
2651
|
+
* @returns an immutable snapshot that preserves the identity.
|
|
2652
|
+
*/
|
|
2653
|
+
function freezeMessage(message) {
|
|
2654
|
+
return deepFreeze(structuredClone(message));
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Create one identified message and freeze it before publication.
|
|
2658
|
+
* @param input - complete role, content, and source for a new message.
|
|
2659
|
+
* @returns an immutable message with a fresh stable identity.
|
|
2660
|
+
*/
|
|
2661
|
+
function createMessage(input) {
|
|
2662
|
+
return freezeMessage({
|
|
2663
|
+
...input,
|
|
2664
|
+
id: MessageId(crypto.randomUUID())
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Create one identified user-role message and freeze it before publication.
|
|
2669
|
+
* @param input - complete content and source for a new user message.
|
|
2670
|
+
* @returns an immutable user message with a fresh stable identity.
|
|
2671
|
+
*/
|
|
2672
|
+
function createUserMessage(input) {
|
|
2673
|
+
return createMessage({
|
|
2674
|
+
...input,
|
|
2675
|
+
role: "user"
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
//#endregion
|
|
2679
|
+
//#region ../../../vendor/cosmokit/src/misc.ts
|
|
2680
|
+
/** Return true when a value is `null` or `undefined`. */
|
|
2681
|
+
function isNullable(value) {
|
|
2682
|
+
return value === null || value === void 0;
|
|
2683
|
+
}
|
|
2684
|
+
/** Return true for non-array object values. */
|
|
2685
|
+
function isPlainObject(data) {
|
|
2686
|
+
return data && typeof data === "object" && !Array.isArray(data);
|
|
2687
|
+
}
|
|
2688
|
+
/** Filter object entries and return a new object. */
|
|
2689
|
+
function filterKeys(object, filter) {
|
|
2690
|
+
return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
|
|
2691
|
+
}
|
|
2692
|
+
/** Map object values while preserving the original key set. */
|
|
2693
|
+
function mapValues(object, transform) {
|
|
2694
|
+
return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
|
|
2695
|
+
}
|
|
2696
|
+
/** Pick selected keys from an object, optionally including `undefined` values. */
|
|
2697
|
+
function pick(source, keys, forced) {
|
|
2698
|
+
if (!keys) return { ...source };
|
|
2699
|
+
const result = {};
|
|
2700
|
+
for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
|
|
2701
|
+
return result;
|
|
2702
|
+
}
|
|
2703
|
+
//#endregion
|
|
2704
|
+
//#region ../../../vendor/cosmokit/src/types.ts
|
|
2705
|
+
/** Test values using `instanceof` with a `toStringTag` fallback. */
|
|
2706
|
+
function is(type, value) {
|
|
2707
|
+
if (arguments.length === 1) return (value) => is(type, value);
|
|
2708
|
+
return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
|
|
2709
|
+
}
|
|
2710
|
+
function isArrayBufferLike(value) {
|
|
2711
|
+
return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
|
|
2712
|
+
}
|
|
2713
|
+
function isArrayBufferSource(value) {
|
|
2714
|
+
return isArrayBufferLike(value) || ArrayBuffer.isView(value);
|
|
2715
|
+
}
|
|
2716
|
+
let Binary;
|
|
2717
|
+
(function(_Binary) {
|
|
2718
|
+
_Binary.is = isArrayBufferLike;
|
|
2719
|
+
_Binary.isSource = isArrayBufferSource;
|
|
2720
|
+
function fromSource(source) {
|
|
2721
|
+
if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
|
|
2722
|
+
else return source;
|
|
2723
|
+
}
|
|
2724
|
+
_Binary.fromSource = fromSource;
|
|
2725
|
+
function toBase64(source) {
|
|
2726
|
+
source = fromSource(source);
|
|
2727
|
+
if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
|
|
2728
|
+
let binary = "";
|
|
2729
|
+
const bytes = new Uint8Array(source);
|
|
2730
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
|
|
2731
|
+
return btoa(binary);
|
|
2732
|
+
}
|
|
2733
|
+
_Binary.toBase64 = toBase64;
|
|
2734
|
+
function fromBase64(source) {
|
|
2735
|
+
if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
|
|
2736
|
+
return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
|
|
2737
|
+
}
|
|
2738
|
+
_Binary.fromBase64 = fromBase64;
|
|
2739
|
+
function toHex(source) {
|
|
2740
|
+
source = fromSource(source);
|
|
2741
|
+
if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
|
|
2742
|
+
return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2743
|
+
}
|
|
2744
|
+
_Binary.toHex = toHex;
|
|
2745
|
+
function fromHex(source) {
|
|
2746
|
+
if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
|
|
2747
|
+
const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
|
|
2748
|
+
const buffer = [];
|
|
2749
|
+
for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
|
|
2750
|
+
return Uint8Array.from(buffer).buffer;
|
|
2751
|
+
}
|
|
2752
|
+
_Binary.fromHex = fromHex;
|
|
2753
|
+
})(Binary || (Binary = {}));
|
|
2754
|
+
Binary.fromBase64;
|
|
2755
|
+
Binary.toBase64;
|
|
2756
|
+
Binary.fromHex;
|
|
2757
|
+
Binary.toHex;
|
|
2758
|
+
/** Deep-clone common JavaScript values while preserving prototypes and cycles. */
|
|
2759
|
+
function clone(source, refs = /* @__PURE__ */ new Map()) {
|
|
2760
|
+
if (!source || typeof source !== "object") return source;
|
|
2761
|
+
if (is("Date", source)) return new Date(source.valueOf());
|
|
2762
|
+
if (is("RegExp", source)) return new RegExp(source.source, source.flags);
|
|
2763
|
+
if (isArrayBufferLike(source)) return source.slice(0);
|
|
2764
|
+
if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
|
|
2765
|
+
const cached = refs.get(source);
|
|
2766
|
+
if (cached) return cached;
|
|
2767
|
+
if (Array.isArray(source)) {
|
|
2768
|
+
const result = [];
|
|
2769
|
+
refs.set(source, result);
|
|
2770
|
+
source.forEach((value, index) => {
|
|
2771
|
+
result[index] = Reflect.apply(clone, null, [value, refs]);
|
|
2772
|
+
});
|
|
2773
|
+
return result;
|
|
2774
|
+
}
|
|
2775
|
+
const result = Object.create(Object.getPrototypeOf(source));
|
|
2776
|
+
refs.set(source, result);
|
|
2777
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
2778
|
+
const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
|
|
2779
|
+
if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
|
|
2780
|
+
Reflect.defineProperty(result, key, descriptor);
|
|
2781
|
+
}
|
|
2782
|
+
return result;
|
|
2783
|
+
}
|
|
2784
|
+
/** Deeply compare arrays, dates, regexps, buffers, and plain object fields. */
|
|
2785
|
+
function deepEqual(a, b, strict) {
|
|
2786
|
+
if (a === b) return true;
|
|
2787
|
+
if (!strict && isNullable(a) && isNullable(b)) return true;
|
|
2788
|
+
if (typeof a !== typeof b) return false;
|
|
2789
|
+
if (typeof a !== "object") return false;
|
|
2790
|
+
if (!a || !b) return false;
|
|
2791
|
+
function check(test, then) {
|
|
2792
|
+
return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
|
|
2793
|
+
}
|
|
2794
|
+
return check(Array.isArray, (a, b) => a.length === b.length && a.every((item, index) => deepEqual(item, b[index]))) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
|
|
2795
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
2796
|
+
const viewA = new Uint8Array(a);
|
|
2797
|
+
const viewB = new Uint8Array(b);
|
|
2798
|
+
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
2799
|
+
return true;
|
|
2800
|
+
}) ?? Object.keys({
|
|
2801
|
+
...a,
|
|
2802
|
+
...b
|
|
2803
|
+
}).every((key) => deepEqual(a[key], b[key], strict));
|
|
2804
|
+
}
|
|
2805
|
+
//#endregion
|
|
2806
|
+
//#region ../../../vendor/cosmokit/src/time.ts
|
|
2807
|
+
let Time;
|
|
2808
|
+
(function(_Time) {
|
|
2809
|
+
_Time.millisecond = 1;
|
|
2810
|
+
const second = _Time.second = 1e3;
|
|
2811
|
+
const minute = _Time.minute = second * 60;
|
|
2812
|
+
const hour = _Time.hour = minute * 60;
|
|
2813
|
+
const day = _Time.day = hour * 24;
|
|
2814
|
+
const week = _Time.week = day * 7;
|
|
2815
|
+
let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
2816
|
+
function setTimezoneOffset(offset) {
|
|
2817
|
+
timezoneOffset = offset;
|
|
2818
|
+
}
|
|
2819
|
+
_Time.setTimezoneOffset = setTimezoneOffset;
|
|
2820
|
+
function getTimezoneOffset() {
|
|
2821
|
+
return timezoneOffset;
|
|
2822
|
+
}
|
|
2823
|
+
_Time.getTimezoneOffset = getTimezoneOffset;
|
|
2824
|
+
function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
|
|
2825
|
+
if (typeof date === "number") date = new Date(date);
|
|
2826
|
+
if (offset === void 0) offset = timezoneOffset;
|
|
2827
|
+
return Math.floor((date.valueOf() / minute - offset) / 1440);
|
|
2828
|
+
}
|
|
2829
|
+
_Time.getDateNumber = getDateNumber;
|
|
2830
|
+
function fromDateNumber(value, offset) {
|
|
2831
|
+
const date = new Date(value * day);
|
|
2832
|
+
if (offset === void 0) offset = timezoneOffset;
|
|
2833
|
+
return new Date(+date + offset * minute);
|
|
2834
|
+
}
|
|
2835
|
+
_Time.fromDateNumber = fromDateNumber;
|
|
2836
|
+
const numeric = /\d+(?:\.\d+)?/.source;
|
|
2837
|
+
const timeRegExp = new RegExp(`^${[
|
|
2838
|
+
"w(?:eek(?:s)?)?",
|
|
2839
|
+
"d(?:ay(?:s)?)?",
|
|
2840
|
+
"h(?:our(?:s)?)?",
|
|
2841
|
+
"m(?:in(?:ute)?(?:s)?)?",
|
|
2842
|
+
"s(?:ec(?:ond)?(?:s)?)?"
|
|
2843
|
+
].map((unit) => `(${numeric}${unit})?`).join("")}$`);
|
|
2844
|
+
function parseTime(source) {
|
|
2845
|
+
const capture = timeRegExp.exec(source);
|
|
2846
|
+
if (!capture) return 0;
|
|
2847
|
+
return (parseFloat(capture[1]) * week || 0) + (parseFloat(capture[2]) * day || 0) + (parseFloat(capture[3]) * hour || 0) + (parseFloat(capture[4]) * minute || 0) + (parseFloat(capture[5]) * second || 0);
|
|
2848
|
+
}
|
|
2849
|
+
_Time.parseTime = parseTime;
|
|
2850
|
+
function parseDate(date) {
|
|
2851
|
+
const parsed = parseTime(date);
|
|
2852
|
+
if (parsed) date = Date.now() + parsed;
|
|
2853
|
+
else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
|
|
2854
|
+
else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
|
|
2855
|
+
return date ? new Date(date) : /* @__PURE__ */ new Date();
|
|
2856
|
+
}
|
|
2857
|
+
_Time.parseDate = parseDate;
|
|
2858
|
+
function format(ms) {
|
|
2859
|
+
const abs = Math.abs(ms);
|
|
2860
|
+
if (abs >= day - hour / 2) return Math.round(ms / day) + "d";
|
|
2861
|
+
else if (abs >= hour - minute / 2) return Math.round(ms / hour) + "h";
|
|
2862
|
+
else if (abs >= minute - second / 2) return Math.round(ms / minute) + "m";
|
|
2863
|
+
else if (abs >= second) return Math.round(ms / second) + "s";
|
|
2864
|
+
return ms + "ms";
|
|
2865
|
+
}
|
|
2866
|
+
_Time.format = format;
|
|
2867
|
+
function toDigits(source, length = 2) {
|
|
2868
|
+
return source.toString().padStart(length, "0");
|
|
2869
|
+
}
|
|
2870
|
+
_Time.toDigits = toDigits;
|
|
2871
|
+
function template(template, time = /* @__PURE__ */ new Date()) {
|
|
2872
|
+
return template.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
|
|
2873
|
+
}
|
|
2874
|
+
_Time.template = template;
|
|
2875
|
+
})(Time || (Time = {}));
|
|
2876
|
+
//#endregion
|
|
2877
|
+
//#region ../../../vendor/schemastery/src/index.ts
|
|
2878
|
+
const kSchema = Symbol.for("schemastery");
|
|
2879
|
+
const kValidationError = Symbol.for("ValidationError");
|
|
2880
|
+
globalThis.__schemastery_index__ ??= 0;
|
|
2881
|
+
globalThis.__schemastery_refs__ = void 0;
|
|
2882
|
+
var ValidationError = class extends TypeError {
|
|
2883
|
+
options;
|
|
2884
|
+
name = "ValidationError";
|
|
2885
|
+
constructor(message, options) {
|
|
2886
|
+
let prefix = "$";
|
|
2887
|
+
for (const segment of options.path || []) if (typeof segment === "string") prefix += "." + segment;
|
|
2888
|
+
else if (typeof segment === "number") prefix += "[" + segment + "]";
|
|
2889
|
+
else if (typeof segment === "symbol") prefix += `[Symbol(${segment.toString()})]`;
|
|
2890
|
+
if (prefix.startsWith(".")) prefix = prefix.slice(1);
|
|
2891
|
+
super((prefix === "$" ? "" : `${prefix} `) + message);
|
|
2892
|
+
this.options = options;
|
|
2893
|
+
}
|
|
2894
|
+
static is(error) {
|
|
2895
|
+
return !!error?.[kValidationError];
|
|
2896
|
+
}
|
|
2897
|
+
};
|
|
2898
|
+
Object.defineProperty(ValidationError.prototype, kValidationError, { value: true });
|
|
2899
|
+
const Schema = function(options) {
|
|
2900
|
+
const schema = function(data, options = {}) {
|
|
2901
|
+
return Schema.resolve(data, schema, options)[0];
|
|
2902
|
+
};
|
|
2903
|
+
if (options.refs) {
|
|
2904
|
+
const refs = mapValues(options.refs, (options) => new Schema(options));
|
|
2905
|
+
const getRef = (uid) => refs[uid];
|
|
2906
|
+
for (const key in refs) {
|
|
2907
|
+
const options = refs[key];
|
|
2908
|
+
options.sKey = getRef(options.sKey);
|
|
2909
|
+
options.inner = getRef(options.inner);
|
|
2910
|
+
options.list = options.list && options.list.map(getRef);
|
|
2911
|
+
options.dict = options.dict && mapValues(options.dict, getRef);
|
|
2912
|
+
}
|
|
2913
|
+
return refs[options.uid];
|
|
2914
|
+
}
|
|
2915
|
+
Object.assign(schema, options);
|
|
2916
|
+
if (typeof schema.callback === "string") try {
|
|
2917
|
+
schema.callback = new Function("return " + schema.callback)();
|
|
2918
|
+
} catch {}
|
|
2919
|
+
Object.defineProperty(schema, "uid", { value: globalThis.__schemastery_index__++ });
|
|
2920
|
+
Object.setPrototypeOf(schema, Schema.prototype);
|
|
2921
|
+
schema.meta ||= {};
|
|
2922
|
+
schema.toString = schema.toString.bind(schema);
|
|
2923
|
+
return schema;
|
|
2924
|
+
};
|
|
2925
|
+
Schema.prototype = Object.create(Function.prototype);
|
|
2926
|
+
Schema.prototype[kSchema] = true;
|
|
2927
|
+
Object.defineProperty(Schema.prototype, "~standard", { get() {
|
|
2928
|
+
return {
|
|
2929
|
+
version: 1,
|
|
2930
|
+
vendor: "schemastery",
|
|
2931
|
+
validate: (value) => {
|
|
2932
|
+
try {
|
|
2933
|
+
return { value: Schema.resolve(value, this, {})[0] };
|
|
2934
|
+
} catch (error) {
|
|
2935
|
+
if (ValidationError.is(error)) return { issues: [{
|
|
2936
|
+
message: error.message,
|
|
2937
|
+
path: error.options.path
|
|
2938
|
+
}] };
|
|
2939
|
+
throw error;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
};
|
|
2943
|
+
} });
|
|
2944
|
+
Schema.ValidationError = ValidationError;
|
|
2945
|
+
Schema.prototype.toJSON = function toJSON() {
|
|
2946
|
+
if (globalThis.__schemastery_refs__) {
|
|
2947
|
+
globalThis.__schemastery_refs__[this.uid] ??= JSON.parse(JSON.stringify({ ...this }));
|
|
2948
|
+
return this.uid;
|
|
2949
|
+
}
|
|
2950
|
+
globalThis.__schemastery_refs__ = { [this.uid]: { ...this } };
|
|
2951
|
+
globalThis.__schemastery_refs__[this.uid] = JSON.parse(JSON.stringify({ ...this }));
|
|
2952
|
+
const result = {
|
|
2953
|
+
uid: this.uid,
|
|
2954
|
+
refs: globalThis.__schemastery_refs__
|
|
2955
|
+
};
|
|
2956
|
+
globalThis.__schemastery_refs__ = void 0;
|
|
2957
|
+
return result;
|
|
2958
|
+
};
|
|
2959
|
+
Schema.prototype.set = function set(key, value) {
|
|
2960
|
+
this.dict[key] = value;
|
|
2961
|
+
return this;
|
|
2962
|
+
};
|
|
2963
|
+
Schema.prototype.push = function push(value) {
|
|
2964
|
+
this.list.push(value);
|
|
2965
|
+
return this;
|
|
2966
|
+
};
|
|
2967
|
+
function mergeDesc(original, messages) {
|
|
2968
|
+
const result = typeof original === "string" ? { "": original } : { ...original };
|
|
2969
|
+
for (const locale in messages) {
|
|
2970
|
+
const value = messages[locale];
|
|
2971
|
+
if (value?.$description || value?.$desc) result[locale] = value.$description || value.$desc;
|
|
2972
|
+
else if (typeof value === "string") result[locale] = value;
|
|
2973
|
+
}
|
|
2974
|
+
return result;
|
|
2975
|
+
}
|
|
2976
|
+
function getInner(value) {
|
|
2977
|
+
return value?.$value ?? value?.$inner;
|
|
2978
|
+
}
|
|
2979
|
+
function extractKeys(data) {
|
|
2980
|
+
return filterKeys(data ?? {}, (key) => !key.startsWith("$"));
|
|
2981
|
+
}
|
|
2982
|
+
Schema.prototype.i18n = function i18n(messages) {
|
|
2983
|
+
const schema = Schema(this);
|
|
2984
|
+
const desc = mergeDesc(schema.meta.description, messages);
|
|
2985
|
+
if (Object.keys(desc).length) schema.meta.description = desc;
|
|
2986
|
+
if (schema.dict) schema.dict = mapValues(schema.dict, (inner, key) => {
|
|
2987
|
+
return inner.i18n(mapValues(messages, (data) => getInner(data)?.[key] ?? data?.[key]));
|
|
2988
|
+
});
|
|
2989
|
+
if (schema.list) schema.list = schema.list.map((inner, index) => {
|
|
2990
|
+
return inner.i18n(mapValues(messages, (data = {}) => {
|
|
2991
|
+
if (Array.isArray(getInner(data))) return getInner(data)[index];
|
|
2992
|
+
if (Array.isArray(data)) return data[index];
|
|
2993
|
+
return extractKeys(data);
|
|
2994
|
+
}));
|
|
2995
|
+
});
|
|
2996
|
+
if (schema.inner) schema.inner = schema.inner.i18n(mapValues(messages, (data) => {
|
|
2997
|
+
if (getInner(data)) return getInner(data);
|
|
2998
|
+
return extractKeys(data);
|
|
2999
|
+
}));
|
|
3000
|
+
if (schema.sKey) schema.sKey = schema.sKey.i18n(mapValues(messages, (data) => data?.$key));
|
|
3001
|
+
return schema;
|
|
3002
|
+
};
|
|
3003
|
+
Schema.prototype.extra = function extra(key, value) {
|
|
3004
|
+
const schema = Schema(this);
|
|
3005
|
+
schema.meta = {
|
|
3006
|
+
...schema.meta,
|
|
3007
|
+
[key]: value
|
|
3008
|
+
};
|
|
3009
|
+
return schema;
|
|
3010
|
+
};
|
|
3011
|
+
for (const key of [
|
|
3012
|
+
"required",
|
|
3013
|
+
"disabled",
|
|
3014
|
+
"collapse",
|
|
3015
|
+
"hidden",
|
|
3016
|
+
"loose"
|
|
3017
|
+
]) Object.assign(Schema.prototype, { [key](value = true) {
|
|
3018
|
+
const schema = Schema(this);
|
|
3019
|
+
schema.meta = {
|
|
3020
|
+
...schema.meta,
|
|
3021
|
+
[key]: value
|
|
3022
|
+
};
|
|
3023
|
+
return schema;
|
|
3024
|
+
} });
|
|
3025
|
+
Schema.prototype.deprecated = function deprecated() {
|
|
3026
|
+
const schema = Schema(this);
|
|
3027
|
+
schema.meta.badges ||= [];
|
|
3028
|
+
schema.meta.badges.push({
|
|
3029
|
+
text: "deprecated",
|
|
3030
|
+
type: "danger"
|
|
3031
|
+
});
|
|
3032
|
+
return schema;
|
|
3033
|
+
};
|
|
3034
|
+
Schema.prototype.experimental = function experimental() {
|
|
3035
|
+
const schema = Schema(this);
|
|
3036
|
+
schema.meta.badges ||= [];
|
|
3037
|
+
schema.meta.badges.push({
|
|
3038
|
+
text: "experimental",
|
|
3039
|
+
type: "warning"
|
|
3040
|
+
});
|
|
3041
|
+
return schema;
|
|
3042
|
+
};
|
|
3043
|
+
Schema.prototype.pattern = function pattern(regexp) {
|
|
3044
|
+
const schema = Schema(this);
|
|
3045
|
+
const pattern = pick(regexp, ["source", "flags"]);
|
|
3046
|
+
schema.meta = {
|
|
3047
|
+
...schema.meta,
|
|
3048
|
+
pattern
|
|
3049
|
+
};
|
|
3050
|
+
return schema;
|
|
3051
|
+
};
|
|
3052
|
+
Schema.prototype.simplify = function simplify(value) {
|
|
3053
|
+
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
3054
|
+
if (isNullable(value)) return value;
|
|
3055
|
+
if (this.type === "object" || this.type === "dict") {
|
|
3056
|
+
const result = {};
|
|
3057
|
+
for (const key in value) {
|
|
3058
|
+
const item = (this.type === "object" ? this.dict[key] : this.inner)?.simplify(value[key]);
|
|
3059
|
+
if (this.type === "dict" || !isNullable(item)) result[key] = item;
|
|
3060
|
+
}
|
|
3061
|
+
if (deepEqual(result, this.meta.default, this.type === "dict")) return null;
|
|
3062
|
+
return result;
|
|
3063
|
+
} else if (this.type === "array" || this.type === "tuple") {
|
|
3064
|
+
const result = [];
|
|
3065
|
+
value.forEach((value, index) => {
|
|
3066
|
+
const schema = this.type === "array" ? this.inner : this.list[index];
|
|
3067
|
+
const item = schema ? schema.simplify(value) : value;
|
|
3068
|
+
result.push(item);
|
|
3069
|
+
});
|
|
3070
|
+
return result;
|
|
3071
|
+
} else if (this.type === "intersect") {
|
|
3072
|
+
const result = {};
|
|
3073
|
+
for (const item of this.list) Object.assign(result, item.simplify(value));
|
|
3074
|
+
return result;
|
|
3075
|
+
} else if (this.type === "union") for (const schema of this.list) try {
|
|
3076
|
+
Schema.resolve(value, schema, {});
|
|
3077
|
+
return schema.simplify(value);
|
|
3078
|
+
} catch {}
|
|
3079
|
+
return value;
|
|
3080
|
+
};
|
|
3081
|
+
Schema.prototype.toString = function toString(inline) {
|
|
3082
|
+
return formatters[this.type]?.(this, inline) ?? `Schema<${this.type}>`;
|
|
3083
|
+
};
|
|
3084
|
+
Schema.prototype.role = function role(role, extra) {
|
|
3085
|
+
const schema = Schema(this);
|
|
3086
|
+
schema.meta = {
|
|
3087
|
+
...schema.meta,
|
|
3088
|
+
role,
|
|
3089
|
+
extra
|
|
3090
|
+
};
|
|
3091
|
+
return schema;
|
|
3092
|
+
};
|
|
3093
|
+
for (const key of [
|
|
3094
|
+
"default",
|
|
3095
|
+
"link",
|
|
3096
|
+
"comment",
|
|
3097
|
+
"description",
|
|
3098
|
+
"max",
|
|
3099
|
+
"min",
|
|
3100
|
+
"step"
|
|
3101
|
+
]) Object.assign(Schema.prototype, { [key](value) {
|
|
3102
|
+
const schema = Schema(this);
|
|
3103
|
+
schema.meta = {
|
|
3104
|
+
...schema.meta,
|
|
3105
|
+
[key]: value
|
|
3106
|
+
};
|
|
3107
|
+
return schema;
|
|
3108
|
+
} });
|
|
3109
|
+
const resolvers = {};
|
|
3110
|
+
Schema.extend = function extend(type, resolve) {
|
|
3111
|
+
resolvers[type] = resolve;
|
|
3112
|
+
};
|
|
3113
|
+
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
3114
|
+
if (!schema) return [data];
|
|
3115
|
+
if (options.ignore?.(data, schema)) return [data];
|
|
3116
|
+
if (isNullable(data) && schema.type !== "lazy") {
|
|
3117
|
+
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
3118
|
+
let current = schema;
|
|
3119
|
+
let fallback = schema.meta.default;
|
|
3120
|
+
while (current?.type === "intersect" && isNullable(fallback)) {
|
|
3121
|
+
current = current.list[0];
|
|
3122
|
+
fallback = current?.meta.default;
|
|
3123
|
+
}
|
|
3124
|
+
if (isNullable(fallback)) return [data];
|
|
3125
|
+
data = clone(fallback);
|
|
3126
|
+
}
|
|
3127
|
+
const callback = resolvers[schema.type];
|
|
3128
|
+
if (!callback) throw new ValidationError(`unsupported type "${schema.type}"`, options);
|
|
3129
|
+
try {
|
|
3130
|
+
return callback(data, schema, options, strict);
|
|
3131
|
+
} catch (error) {
|
|
3132
|
+
if (!schema.meta.loose) throw error;
|
|
3133
|
+
return [schema.meta.default];
|
|
3134
|
+
}
|
|
3135
|
+
};
|
|
3136
|
+
Schema.from = function from(source) {
|
|
3137
|
+
if (isNullable(source)) return Schema.any();
|
|
3138
|
+
else if ([
|
|
3139
|
+
"string",
|
|
3140
|
+
"number",
|
|
3141
|
+
"boolean"
|
|
3142
|
+
].includes(typeof source)) return Schema.const(source).required();
|
|
3143
|
+
else if (source[kSchema]) return source;
|
|
3144
|
+
else if (typeof source === "function") switch (source) {
|
|
3145
|
+
case String: return Schema.string().required();
|
|
3146
|
+
case Number: return Schema.number().required();
|
|
3147
|
+
case Boolean: return Schema.boolean().required();
|
|
3148
|
+
case Function: return Schema.function().required();
|
|
3149
|
+
default: return Schema.is(source).required();
|
|
3150
|
+
}
|
|
3151
|
+
else throw new TypeError(`cannot infer schema from ${source}`);
|
|
3152
|
+
};
|
|
3153
|
+
Schema.lazy = function lazy(builder) {
|
|
3154
|
+
const toJSON = () => {
|
|
3155
|
+
if (!schema.inner[kSchema]) {
|
|
3156
|
+
schema.inner = schema.builder();
|
|
3157
|
+
schema.inner.meta = {
|
|
3158
|
+
...schema.meta,
|
|
3159
|
+
...schema.inner.meta
|
|
3160
|
+
};
|
|
3161
|
+
}
|
|
3162
|
+
return schema.inner.toJSON();
|
|
3163
|
+
};
|
|
3164
|
+
const schema = new Schema({
|
|
3165
|
+
type: "lazy",
|
|
3166
|
+
builder,
|
|
3167
|
+
inner: { toJSON }
|
|
3168
|
+
});
|
|
3169
|
+
return schema;
|
|
3170
|
+
};
|
|
3171
|
+
Schema.natural = function natural() {
|
|
3172
|
+
return Schema.number().step(1).min(0);
|
|
3173
|
+
};
|
|
3174
|
+
Schema.percent = function percent() {
|
|
3175
|
+
return Schema.number().step(.01).min(0).max(1).role("slider");
|
|
3176
|
+
};
|
|
3177
|
+
Schema.date = function date() {
|
|
3178
|
+
return Schema.union([Schema.is(Date), Schema.transform(Schema.string().role("datetime"), (value, options) => {
|
|
3179
|
+
const date = new Date(value);
|
|
3180
|
+
if (isNaN(+date)) throw new ValidationError(`invalid date "${value}"`, options);
|
|
3181
|
+
return date;
|
|
3182
|
+
}, true)]);
|
|
3183
|
+
};
|
|
3184
|
+
Schema.regExp = function regExp(flag = "") {
|
|
3185
|
+
return Schema.union([Schema.is(RegExp), Schema.transform(Schema.string().role("regexp", { flag }), (value, options) => {
|
|
3186
|
+
try {
|
|
3187
|
+
return new RegExp(value, flag);
|
|
3188
|
+
} catch (e) {
|
|
3189
|
+
throw new ValidationError(e.message, options);
|
|
3190
|
+
}
|
|
3191
|
+
}, true)]);
|
|
3192
|
+
};
|
|
3193
|
+
Schema.arrayBuffer = function arrayBuffer(encoding) {
|
|
3194
|
+
return Schema.union([
|
|
3195
|
+
Schema.is(ArrayBuffer),
|
|
3196
|
+
Schema.is(SharedArrayBuffer),
|
|
3197
|
+
Schema.transform(Schema.any(), (value, options) => {
|
|
3198
|
+
if (Binary.isSource(value)) return Binary.fromSource(value);
|
|
3199
|
+
throw new ValidationError(`expected ArrayBufferSource but got ${value}`, options);
|
|
3200
|
+
}, true),
|
|
3201
|
+
...encoding ? [Schema.transform(Schema.string(), (value, options) => {
|
|
3202
|
+
try {
|
|
3203
|
+
return encoding === "base64" ? Binary.fromBase64(value) : Binary.fromHex(value);
|
|
3204
|
+
} catch (e) {
|
|
3205
|
+
throw new ValidationError(e.message, options);
|
|
3206
|
+
}
|
|
3207
|
+
}, true)] : []
|
|
3208
|
+
]);
|
|
3209
|
+
};
|
|
3210
|
+
Schema.extend("lazy", (data, schema, options, strict) => {
|
|
3211
|
+
if (!schema.inner[kSchema]) {
|
|
3212
|
+
schema.inner = schema.builder();
|
|
3213
|
+
schema.inner.meta = {
|
|
3214
|
+
...schema.meta,
|
|
3215
|
+
...schema.inner.meta
|
|
3216
|
+
};
|
|
3217
|
+
}
|
|
3218
|
+
return Schema.resolve(data, schema.inner, options, strict);
|
|
3219
|
+
});
|
|
3220
|
+
Schema.extend("any", (data) => {
|
|
3221
|
+
return [data];
|
|
3222
|
+
});
|
|
3223
|
+
Schema.extend("never", (data, _, options) => {
|
|
3224
|
+
throw new ValidationError(`expected nullable but got ${data}`, options);
|
|
3225
|
+
});
|
|
3226
|
+
Schema.extend("const", (data, { value }, options) => {
|
|
3227
|
+
if (deepEqual(data, value)) return [value];
|
|
3228
|
+
throw new ValidationError(`expected ${value} but got ${data}`, options);
|
|
3229
|
+
});
|
|
3230
|
+
function checkWithinRange(data, meta, description, options, skipMin = false) {
|
|
3231
|
+
const { max = Infinity, min = -Infinity } = meta;
|
|
3232
|
+
if (data > max) throw new ValidationError(`expected ${description} <= ${max} but got ${data}`, options);
|
|
3233
|
+
if (data < min && !skipMin) throw new ValidationError(`expected ${description} >= ${min} but got ${data}`, options);
|
|
3234
|
+
}
|
|
3235
|
+
Schema.extend("string", (data, { meta }, options) => {
|
|
3236
|
+
if (typeof data !== "string") throw new ValidationError(`expected string but got ${data}`, options);
|
|
3237
|
+
if (meta.pattern) {
|
|
3238
|
+
const regexp = new RegExp(meta.pattern.source, meta.pattern.flags);
|
|
3239
|
+
if (!regexp.test(data)) throw new ValidationError(`expect string to match regexp ${regexp}`, options);
|
|
3240
|
+
}
|
|
3241
|
+
checkWithinRange(data.length, meta, "string length", options);
|
|
3242
|
+
return [data];
|
|
3243
|
+
});
|
|
3244
|
+
function decimalShift(data, digits) {
|
|
3245
|
+
const str = data.toString();
|
|
3246
|
+
if (str.includes("e")) return data * Math.pow(10, digits);
|
|
3247
|
+
const index = str.indexOf(".");
|
|
3248
|
+
if (index === -1) return data * Math.pow(10, digits);
|
|
3249
|
+
const frac = str.slice(index + 1);
|
|
3250
|
+
const integer = str.slice(0, index);
|
|
3251
|
+
if (frac.length <= digits) return +(integer + frac.padEnd(digits, "0"));
|
|
3252
|
+
return +(integer + frac.slice(0, digits) + "." + frac.slice(digits));
|
|
3253
|
+
}
|
|
3254
|
+
function isMultipleOf(data, min, step) {
|
|
3255
|
+
step = Math.abs(step);
|
|
3256
|
+
if (!/^\d+\.\d+$/.test(step.toString())) return (data - min) % step === 0;
|
|
3257
|
+
const index = step.toString().indexOf(".");
|
|
3258
|
+
const digits = step.toString().slice(index + 1).length;
|
|
3259
|
+
return Math.abs(decimalShift(data, digits) - decimalShift(min, digits)) % decimalShift(step, digits) === 0;
|
|
3260
|
+
}
|
|
3261
|
+
Schema.extend("number", (data, { meta }, options) => {
|
|
3262
|
+
if (typeof data !== "number") throw new ValidationError(`expected number but got ${data}`, options);
|
|
3263
|
+
checkWithinRange(data, meta, "number", options);
|
|
3264
|
+
const { step } = meta;
|
|
3265
|
+
if (step && !isMultipleOf(data, meta.min ?? 0, step)) throw new ValidationError(`expected number multiple of ${step} but got ${data}`, options);
|
|
3266
|
+
return [data];
|
|
3267
|
+
});
|
|
3268
|
+
Schema.extend("boolean", (data, _, options) => {
|
|
3269
|
+
if (typeof data === "boolean") return [data];
|
|
3270
|
+
throw new ValidationError(`expected boolean but got ${data}`, options);
|
|
3271
|
+
});
|
|
3272
|
+
Schema.extend("bitset", (data, { bits, meta }, options) => {
|
|
3273
|
+
let value = 0, keys = [];
|
|
3274
|
+
if (typeof data === "number") {
|
|
3275
|
+
value = data;
|
|
3276
|
+
for (const key in bits) if (data & bits[key]) keys.push(key);
|
|
3277
|
+
} else if (Array.isArray(data)) {
|
|
3278
|
+
keys = data;
|
|
3279
|
+
for (const key of keys) {
|
|
3280
|
+
if (typeof key !== "string") throw new ValidationError(`expected string but got ${key}`, options);
|
|
3281
|
+
if (key in bits) value |= bits[key];
|
|
3282
|
+
}
|
|
3283
|
+
} else throw new ValidationError(`expected number or array but got ${data}`, options);
|
|
3284
|
+
if (value === meta.default) return [value];
|
|
3285
|
+
return [value, keys];
|
|
3286
|
+
});
|
|
3287
|
+
Schema.extend("function", (data, _, options) => {
|
|
3288
|
+
if (typeof data === "function") return [data];
|
|
3289
|
+
throw new ValidationError(`expected function but got ${data}`, options);
|
|
3290
|
+
});
|
|
3291
|
+
Schema.extend("is", (data, { constructor }, options) => {
|
|
3292
|
+
if (typeof constructor === "function") {
|
|
3293
|
+
if (data instanceof constructor) return [data];
|
|
3294
|
+
throw new ValidationError(`expected ${constructor.name} but got ${data}`, options);
|
|
3295
|
+
} else {
|
|
3296
|
+
if (isNullable(data)) throw new ValidationError(`expected ${constructor} but got ${data}`, options);
|
|
3297
|
+
let prototype = Object.getPrototypeOf(data);
|
|
3298
|
+
while (prototype) {
|
|
3299
|
+
if (prototype.constructor?.name === constructor) return [data];
|
|
3300
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
3301
|
+
}
|
|
3302
|
+
throw new ValidationError(`expected ${constructor} but got ${data}`, options);
|
|
3303
|
+
}
|
|
3304
|
+
});
|
|
3305
|
+
function property(data, key, schema, options) {
|
|
3306
|
+
try {
|
|
3307
|
+
const [value, adapted] = Schema.resolve(data[key], schema, {
|
|
3308
|
+
...options,
|
|
3309
|
+
path: [...options.path || [], key]
|
|
3310
|
+
});
|
|
3311
|
+
if (adapted !== void 0) data[key] = adapted;
|
|
3312
|
+
return value;
|
|
3313
|
+
} catch (e) {
|
|
3314
|
+
if (!options?.autofix) throw e;
|
|
3315
|
+
delete data[key];
|
|
3316
|
+
return schema.meta.default;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
3320
|
+
if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
|
|
3321
|
+
checkWithinRange(data.length, meta, "array length", options, !isNullable(inner.meta.default));
|
|
3322
|
+
return [data.map((_, index) => property(data, index, inner, options))];
|
|
3323
|
+
});
|
|
3324
|
+
Schema.extend("dict", (data, { inner, sKey }, options, strict) => {
|
|
3325
|
+
if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
|
|
3326
|
+
const result = {};
|
|
3327
|
+
for (const key in data) {
|
|
3328
|
+
let rKey;
|
|
3329
|
+
try {
|
|
3330
|
+
rKey = Schema.resolve(key, sKey, options)[0];
|
|
3331
|
+
} catch (error) {
|
|
3332
|
+
if (strict) continue;
|
|
3333
|
+
throw error;
|
|
3334
|
+
}
|
|
3335
|
+
result[rKey] = property(data, key, inner, options);
|
|
3336
|
+
data[rKey] = data[key];
|
|
3337
|
+
if (key !== rKey) delete data[key];
|
|
3338
|
+
}
|
|
3339
|
+
return [result];
|
|
3340
|
+
});
|
|
3341
|
+
Schema.extend("tuple", (data, { list }, options, strict) => {
|
|
3342
|
+
if (!Array.isArray(data)) throw new ValidationError(`expected array but got ${data}`, options);
|
|
3343
|
+
const result = list.map((inner, index) => property(data, index, inner, options));
|
|
3344
|
+
if (strict) return [result];
|
|
3345
|
+
result.push(...data.slice(list.length));
|
|
3346
|
+
return [result];
|
|
3347
|
+
});
|
|
3348
|
+
function merge(result, data) {
|
|
3349
|
+
for (const key in data) {
|
|
3350
|
+
if (key in result) continue;
|
|
3351
|
+
result[key] = data[key];
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
Schema.extend("object", (data, { dict }, options, strict) => {
|
|
3355
|
+
if (!isPlainObject(data)) throw new ValidationError(`expected object but got ${data}`, options);
|
|
3356
|
+
const result = {};
|
|
3357
|
+
for (const key in dict) {
|
|
3358
|
+
const value = property(data, key, dict[key], options);
|
|
3359
|
+
if (!isNullable(value) || key in data) result[key] = value;
|
|
3360
|
+
}
|
|
3361
|
+
if (!strict) merge(result, data);
|
|
3362
|
+
return [result];
|
|
3363
|
+
});
|
|
3364
|
+
Schema.extend("union", (data, { list, toString }, options, strict) => {
|
|
3365
|
+
const messages = [];
|
|
3366
|
+
for (const inner of list) try {
|
|
3367
|
+
return Schema.resolve(data, inner, options, strict);
|
|
3368
|
+
} catch (error) {
|
|
3369
|
+
messages.push(error);
|
|
3370
|
+
}
|
|
3371
|
+
throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
|
|
3372
|
+
});
|
|
3373
|
+
Schema.extend("intersect", (data, { list, toString }, options, strict) => {
|
|
3374
|
+
if (!list.length) return [data];
|
|
3375
|
+
let result;
|
|
3376
|
+
for (const inner of list) {
|
|
3377
|
+
const value = Schema.resolve(data, inner, options, true)[0];
|
|
3378
|
+
if (isNullable(value)) continue;
|
|
3379
|
+
if (isNullable(result)) result = value;
|
|
3380
|
+
else if (typeof result !== typeof value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
|
|
3381
|
+
else if (typeof value === "object") merge(result ??= {}, value);
|
|
3382
|
+
else if (result !== value) throw new ValidationError(`expected ${toString()} but got ${JSON.stringify(data)}`, options);
|
|
3383
|
+
}
|
|
3384
|
+
if (!strict && isPlainObject(data)) merge(result, data);
|
|
3385
|
+
return [result];
|
|
3386
|
+
});
|
|
3387
|
+
Schema.extend("transform", (data, { inner, callback, preserve }, options) => {
|
|
3388
|
+
const [result, adapted = data] = Schema.resolve(data, inner, options, true);
|
|
3389
|
+
if (preserve) return [callback(result)];
|
|
3390
|
+
else return [callback(result), callback(adapted)];
|
|
3391
|
+
});
|
|
3392
|
+
const formatters = {};
|
|
3393
|
+
function defineMethod(name, keys, format) {
|
|
3394
|
+
formatters[name] = format;
|
|
3395
|
+
Object.assign(Schema, { [name](...args) {
|
|
3396
|
+
const schema = new Schema({ type: name });
|
|
3397
|
+
keys.forEach((key, index) => {
|
|
3398
|
+
switch (key) {
|
|
3399
|
+
case "sKey":
|
|
3400
|
+
schema.sKey = args[index] ?? Schema.string();
|
|
3401
|
+
break;
|
|
3402
|
+
case "inner":
|
|
3403
|
+
schema.inner = Schema.from(args[index]);
|
|
3404
|
+
break;
|
|
3405
|
+
case "list":
|
|
3406
|
+
schema.list = args[index].map(Schema.from);
|
|
3407
|
+
break;
|
|
3408
|
+
case "dict":
|
|
3409
|
+
schema.dict = mapValues(args[index], Schema.from);
|
|
3410
|
+
break;
|
|
3411
|
+
case "bits":
|
|
3412
|
+
schema.bits = {};
|
|
3413
|
+
for (const key in args[index]) {
|
|
3414
|
+
if (typeof args[index][key] !== "number") continue;
|
|
3415
|
+
schema.bits[key] = args[index][key];
|
|
3416
|
+
}
|
|
3417
|
+
break;
|
|
3418
|
+
case "callback": {
|
|
3419
|
+
const callback = schema.callback = args[index];
|
|
3420
|
+
callback["toJSON"] ||= () => callback.toString();
|
|
3421
|
+
break;
|
|
3422
|
+
}
|
|
3423
|
+
case "constructor": {
|
|
3424
|
+
const constructor = schema.constructor = args[index];
|
|
3425
|
+
if (typeof constructor === "function") constructor["toJSON"] ||= () => constructor["name"];
|
|
3426
|
+
break;
|
|
3427
|
+
}
|
|
3428
|
+
default: schema[key] = args[index];
|
|
3429
|
+
}
|
|
3430
|
+
});
|
|
3431
|
+
if (name === "object" || name === "dict") schema.meta.default = {};
|
|
3432
|
+
else if (name === "array" || name === "tuple") schema.meta.default = [];
|
|
3433
|
+
else if (name === "bitset") schema.meta.default = 0;
|
|
3434
|
+
return schema;
|
|
3435
|
+
} });
|
|
3436
|
+
}
|
|
3437
|
+
defineMethod("is", ["constructor"], ({ constructor }) => {
|
|
3438
|
+
if (typeof constructor === "function") return constructor.name;
|
|
3439
|
+
else return constructor;
|
|
3440
|
+
});
|
|
3441
|
+
defineMethod("any", [], () => "any");
|
|
3442
|
+
defineMethod("never", [], () => "never");
|
|
3443
|
+
defineMethod("const", ["value"], ({ value }) => typeof value === "string" ? JSON.stringify(value) : value);
|
|
3444
|
+
defineMethod("string", [], () => "string");
|
|
3445
|
+
defineMethod("number", [], () => "number");
|
|
3446
|
+
defineMethod("boolean", [], () => "boolean");
|
|
3447
|
+
defineMethod("bitset", ["bits"], () => "bitset");
|
|
3448
|
+
defineMethod("function", [], () => "function");
|
|
3449
|
+
defineMethod("array", ["inner"], ({ inner }) => `${inner.toString(true)}[]`);
|
|
3450
|
+
defineMethod("dict", ["inner", "sKey"], ({ inner, sKey }) => `{ [key: ${sKey.toString()}]: ${inner.toString()} }`);
|
|
3451
|
+
defineMethod("tuple", ["list"], ({ list }) => `[${list.map((inner) => inner.toString()).join(", ")}]`);
|
|
3452
|
+
defineMethod("object", ["dict"], ({ dict }) => {
|
|
3453
|
+
if (Object.keys(dict).length === 0) return "{}";
|
|
3454
|
+
return `{ ${Object.entries(dict).map(([key, inner]) => {
|
|
3455
|
+
return `${key}${inner.meta.required ? "" : "?"}: ${inner.toString()}`;
|
|
3456
|
+
}).join(", ")} }`;
|
|
3457
|
+
});
|
|
3458
|
+
defineMethod("union", ["list"], ({ list }, inline) => {
|
|
3459
|
+
const result = list.map(({ toString: format }) => format()).join(" | ");
|
|
3460
|
+
return inline ? `(${result})` : result;
|
|
3461
|
+
});
|
|
3462
|
+
defineMethod("intersect", ["list"], ({ list }) => {
|
|
3463
|
+
return `${list.map((inner) => inner.toString(true)).join(" & ")}`;
|
|
3464
|
+
});
|
|
3465
|
+
defineMethod("transform", [
|
|
3466
|
+
"inner",
|
|
3467
|
+
"callback",
|
|
3468
|
+
"preserve"
|
|
3469
|
+
], ({ inner }, isInner) => inner.toString(isInner));
|
|
3470
|
+
//#endregion
|
|
3471
|
+
//#region ../../util/timeout/src/index.ts
|
|
3472
|
+
/** Largest delay Node schedules without clamping it to one millisecond. */
|
|
3473
|
+
const MAX_TIMER_DELAY_MS = 2147483647;
|
|
3474
|
+
//#endregion
|
|
3475
|
+
//#region ../../llm/llm/src/error.ts
|
|
3476
|
+
/**
|
|
3477
|
+
* Canonical provider-neutral code for a response that completed normally but
|
|
3478
|
+
* carried no content blocks at all. Providers occasionally emit a degenerate
|
|
3479
|
+
* completion (a terminal stop with zero output); adapters classify it as this
|
|
3480
|
+
* failure instead of yielding an empty assistant message, because an empty
|
|
3481
|
+
* message silently ends the turn with nothing for the user or the loop to act
|
|
3482
|
+
* on. The attempt produced nothing durable, so retry policy treats it as safe
|
|
3483
|
+
* to repeat.
|
|
3484
|
+
*/
|
|
3485
|
+
const EMPTY_RESPONSE_CODE = "EMPTY_RESPONSE";
|
|
3486
|
+
new RegExp(String.raw`(?:^|[^a-z0-9])context[\s_-](?:length|window)[\s_-]` + String.raw`(?:exceed(?:ed|s)?|overflow(?:ed)?|limit[\s_-]exceeded)(?:$|[^a-z0-9])`, "i");
|
|
3487
|
+
new RegExp(String.raw`\b(?:request|prompt|input|messages?)\s+(?:is\s+|are\s+)?` + String.raw`too\s+(?:large|long)\s+for\s+(?:(?:this|the)\s+)?` + String.raw`(?:model(?:'s)?\s+)?context(?:\s+window)?\b`, "i");
|
|
3488
|
+
new RegExp(String.raw`\b(?:input|prompt|request|messages?)\b.{0,40}` + String.raw`\b(?:exceed(?:s|ed)?|overflows?|is\s+larger\s+than)\b.{0,40}` + String.raw`\b(?:the\s+)?(?:model(?:'s)?\s+)?context(?:\s+(?:length|window))?\b`, "i");
|
|
3489
|
+
//#endregion
|
|
3490
|
+
//#region ../../llm/llm/src/retry-policy.ts
|
|
3491
|
+
/**
|
|
3492
|
+
* Provider-owned request-retry policy configuration and resolution.
|
|
3493
|
+
*
|
|
3494
|
+
* Adapters expose one resolved policy per registered provider route; the
|
|
3495
|
+
* optional dsh-llm-retry plugin executes it on the agent's failed-step extension point.
|
|
3496
|
+
*
|
|
3497
|
+
* @module @deepseek-ai/dsh-llm/retry-policy
|
|
3498
|
+
*/
|
|
3499
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
3500
|
+
const DEFAULT_INITIAL_DELAY_MS = 500;
|
|
3501
|
+
const DEFAULT_MAX_DELAY_MS = 1e4;
|
|
3502
|
+
const DEFAULT_JITTER_RATIO = .1;
|
|
3503
|
+
const DEFAULT_RETRYABLE_CODES = Object.freeze([
|
|
3504
|
+
EMPTY_RESPONSE_CODE,
|
|
3505
|
+
"RATE_LIMIT",
|
|
3506
|
+
"SERVER",
|
|
3507
|
+
"TIMEOUT",
|
|
3508
|
+
"TRANSPORT"
|
|
3509
|
+
]);
|
|
3510
|
+
const backoffSchema = Schema.object({
|
|
3511
|
+
initialDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
|
|
3512
|
+
maxDelayMs: Schema.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
|
|
3513
|
+
jitterRatio: Schema.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
|
|
3514
|
+
});
|
|
3515
|
+
const normalPolicySchema = Schema.object({
|
|
3516
|
+
mode: Schema.const("normal").required(),
|
|
3517
|
+
maxRetries: Schema.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
|
|
3518
|
+
retryableCodes: Schema.array(Schema.string()).default([...DEFAULT_RETRYABLE_CODES]),
|
|
3519
|
+
backoff: backoffSchema
|
|
3520
|
+
});
|
|
3521
|
+
const alwaysPolicySchema = Schema.object({
|
|
3522
|
+
mode: Schema.const("always").required(),
|
|
3523
|
+
backoff: backoffSchema
|
|
3524
|
+
});
|
|
3525
|
+
Schema.union([normalPolicySchema, alwaysPolicySchema]);
|
|
3526
|
+
//#endregion
|
|
3527
|
+
//#region ../../llm/llm/src/attribution.ts
|
|
3528
|
+
/**
|
|
3529
|
+
* Centralize the non-secret product identity every provider request sends as `User-Agent`, keeping
|
|
3530
|
+
* adapters from drifting. See
|
|
3531
|
+
* `.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md`.
|
|
3532
|
+
*
|
|
3533
|
+
* App-attribution vocabulary for provider requests.
|
|
3534
|
+
* @module @deepseek-ai/dsh-llm/attribution
|
|
3535
|
+
*/
|
|
3536
|
+
const { version } = createRequire(import.meta.url)("../package.json");
|
|
3537
|
+
//#endregion
|
|
2406
3538
|
//#region lib/types/service.js
|
|
2407
3539
|
/**
|
|
2408
3540
|
* Memory library Typert Remote service: the wire face over MemoryCore.
|
|
@@ -2506,6 +3638,12 @@ let MemoryService = (() => {
|
|
|
2506
3638
|
let _humanAckDiary_decorators;
|
|
2507
3639
|
let _humanSetConcernStatus_decorators;
|
|
2508
3640
|
let _humanDeleteConcern_decorators;
|
|
3641
|
+
let _generateSkillDraft_decorators;
|
|
3642
|
+
let _reviewSkill_decorators;
|
|
3643
|
+
let _publishSkill_decorators;
|
|
3644
|
+
let _listSkillArtifacts_decorators;
|
|
3645
|
+
let _getSkillArtifact_decorators;
|
|
3646
|
+
let _isSkillCandidate_decorators;
|
|
2509
3647
|
return class MemoryService extends _classSuper {
|
|
2510
3648
|
static {
|
|
2511
3649
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
@@ -2554,6 +3692,12 @@ let MemoryService = (() => {
|
|
|
2554
3692
|
_humanAckDiary_decorators = [Remote("humanAckDiary")];
|
|
2555
3693
|
_humanSetConcernStatus_decorators = [Remote("humanSetConcernStatus")];
|
|
2556
3694
|
_humanDeleteConcern_decorators = [Remote("humanDeleteConcern")];
|
|
3695
|
+
_generateSkillDraft_decorators = [Remote("generateSkillDraft")];
|
|
3696
|
+
_reviewSkill_decorators = [Remote("reviewSkill")];
|
|
3697
|
+
_publishSkill_decorators = [Remote("publishSkill")];
|
|
3698
|
+
_listSkillArtifacts_decorators = [Remote("listSkillArtifacts")];
|
|
3699
|
+
_getSkillArtifact_decorators = [Remote("getSkillArtifact")];
|
|
3700
|
+
_isSkillCandidate_decorators = [Remote("isSkillCandidate")];
|
|
2557
3701
|
__esDecorate(this, null, _refine_decorators, {
|
|
2558
3702
|
kind: "method",
|
|
2559
3703
|
name: "refine",
|
|
@@ -3049,6 +4193,72 @@ let MemoryService = (() => {
|
|
|
3049
4193
|
},
|
|
3050
4194
|
metadata: _metadata
|
|
3051
4195
|
}, null, _instanceExtraInitializers);
|
|
4196
|
+
__esDecorate(this, null, _generateSkillDraft_decorators, {
|
|
4197
|
+
kind: "method",
|
|
4198
|
+
name: "generateSkillDraft",
|
|
4199
|
+
static: false,
|
|
4200
|
+
private: false,
|
|
4201
|
+
access: {
|
|
4202
|
+
has: (obj) => "generateSkillDraft" in obj,
|
|
4203
|
+
get: (obj) => obj.generateSkillDraft
|
|
4204
|
+
},
|
|
4205
|
+
metadata: _metadata
|
|
4206
|
+
}, null, _instanceExtraInitializers);
|
|
4207
|
+
__esDecorate(this, null, _reviewSkill_decorators, {
|
|
4208
|
+
kind: "method",
|
|
4209
|
+
name: "reviewSkill",
|
|
4210
|
+
static: false,
|
|
4211
|
+
private: false,
|
|
4212
|
+
access: {
|
|
4213
|
+
has: (obj) => "reviewSkill" in obj,
|
|
4214
|
+
get: (obj) => obj.reviewSkill
|
|
4215
|
+
},
|
|
4216
|
+
metadata: _metadata
|
|
4217
|
+
}, null, _instanceExtraInitializers);
|
|
4218
|
+
__esDecorate(this, null, _publishSkill_decorators, {
|
|
4219
|
+
kind: "method",
|
|
4220
|
+
name: "publishSkill",
|
|
4221
|
+
static: false,
|
|
4222
|
+
private: false,
|
|
4223
|
+
access: {
|
|
4224
|
+
has: (obj) => "publishSkill" in obj,
|
|
4225
|
+
get: (obj) => obj.publishSkill
|
|
4226
|
+
},
|
|
4227
|
+
metadata: _metadata
|
|
4228
|
+
}, null, _instanceExtraInitializers);
|
|
4229
|
+
__esDecorate(this, null, _listSkillArtifacts_decorators, {
|
|
4230
|
+
kind: "method",
|
|
4231
|
+
name: "listSkillArtifacts",
|
|
4232
|
+
static: false,
|
|
4233
|
+
private: false,
|
|
4234
|
+
access: {
|
|
4235
|
+
has: (obj) => "listSkillArtifacts" in obj,
|
|
4236
|
+
get: (obj) => obj.listSkillArtifacts
|
|
4237
|
+
},
|
|
4238
|
+
metadata: _metadata
|
|
4239
|
+
}, null, _instanceExtraInitializers);
|
|
4240
|
+
__esDecorate(this, null, _getSkillArtifact_decorators, {
|
|
4241
|
+
kind: "method",
|
|
4242
|
+
name: "getSkillArtifact",
|
|
4243
|
+
static: false,
|
|
4244
|
+
private: false,
|
|
4245
|
+
access: {
|
|
4246
|
+
has: (obj) => "getSkillArtifact" in obj,
|
|
4247
|
+
get: (obj) => obj.getSkillArtifact
|
|
4248
|
+
},
|
|
4249
|
+
metadata: _metadata
|
|
4250
|
+
}, null, _instanceExtraInitializers);
|
|
4251
|
+
__esDecorate(this, null, _isSkillCandidate_decorators, {
|
|
4252
|
+
kind: "method",
|
|
4253
|
+
name: "isSkillCandidate",
|
|
4254
|
+
static: false,
|
|
4255
|
+
private: false,
|
|
4256
|
+
access: {
|
|
4257
|
+
has: (obj) => "isSkillCandidate" in obj,
|
|
4258
|
+
get: (obj) => obj.isSkillCandidate
|
|
4259
|
+
},
|
|
4260
|
+
metadata: _metadata
|
|
4261
|
+
}, null, _instanceExtraInitializers);
|
|
3052
4262
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, {
|
|
3053
4263
|
enumerable: true,
|
|
3054
4264
|
configurable: true,
|
|
@@ -3147,8 +4357,7 @@ let MemoryService = (() => {
|
|
|
3147
4357
|
2. 每条规则说明:什么类型的记忆应该避免提取,以及为什么
|
|
3148
4358
|
3. 规则要具体可操作,不要泛泛而谈
|
|
3149
4359
|
4. 如果删除原因都很相似,合并为更精炼的规则`;
|
|
3150
|
-
const messages = [{
|
|
3151
|
-
role: "user",
|
|
4360
|
+
const messages = [createUserMessage({
|
|
3152
4361
|
content: [{
|
|
3153
4362
|
type: "text",
|
|
3154
4363
|
text: `以下是近期被用户删除的记忆及其删除原因:
|
|
@@ -3156,8 +4365,12 @@ let MemoryService = (() => {
|
|
|
3156
4365
|
${deletionList}
|
|
3157
4366
|
|
|
3158
4367
|
请总结成提取反馈规则。`
|
|
3159
|
-
}]
|
|
3160
|
-
|
|
4368
|
+
}],
|
|
4369
|
+
source: {
|
|
4370
|
+
kind: "plugin",
|
|
4371
|
+
plugin: "dsh-daoing-memory"
|
|
4372
|
+
}
|
|
4373
|
+
})];
|
|
3161
4374
|
let text = "";
|
|
3162
4375
|
const options = {
|
|
3163
4376
|
provider,
|
|
@@ -3341,6 +4554,105 @@ ${deletionList}
|
|
|
3341
4554
|
this.core.humanDeleteConcern(request, "human");
|
|
3342
4555
|
return { deleted: true };
|
|
3343
4556
|
}
|
|
4557
|
+
/** Generate a skill draft from an experience using LLM. */
|
|
4558
|
+
async generateSkillDraft(agent, request) {
|
|
4559
|
+
const experience = this.core["store"].getActiveRevision(request.experienceId);
|
|
4560
|
+
if (experience === void 0) throw new Error(`memory: experience not found: ${request.experienceId}`);
|
|
4561
|
+
const content = await this.generateSkillContentWithLlm(agent, experience, request.form);
|
|
4562
|
+
if (content.length === 0) throw new Error("memory: LLM generated empty skill content");
|
|
4563
|
+
const draftDir = join(process.env.DSH_HOME ?? join(process.cwd(), ".dsh"), "dsh-daoing-memory", "skills");
|
|
4564
|
+
mkdirSync(draftDir, { recursive: true });
|
|
4565
|
+
const ext = request.form === "skill_md" ? ".md" : ".mjs";
|
|
4566
|
+
const draftPath = join(draftDir, `${randomUUID()}${ext}`);
|
|
4567
|
+
writeFileSync(draftPath, content, "utf8");
|
|
4568
|
+
return this.core.createSkillDraft(request.experienceId, request.form, content, draftPath, actorOf(agent));
|
|
4569
|
+
}
|
|
4570
|
+
/** Review (approve/reject) a skill artifact. */
|
|
4571
|
+
reviewSkill(agent, request) {
|
|
4572
|
+
return this.core.reviewSkill(request, actorOf(agent));
|
|
4573
|
+
}
|
|
4574
|
+
/** Publish an approved skill (copy to $DSH_HOME/skills/). */
|
|
4575
|
+
publishSkill(agent, request) {
|
|
4576
|
+
const artifact = this.core.getSkillArtifact(request.id);
|
|
4577
|
+
if (artifact === void 0) throw new Error(`memory: skill artifact not found: ${request.id}`);
|
|
4578
|
+
if (artifact.draftPath === void 0) throw new Error("memory: skill artifact has no draft path");
|
|
4579
|
+
const publishDir = join(process.env.DSH_HOME ?? join(process.cwd(), ".dsh"), "skills");
|
|
4580
|
+
mkdirSync(publishDir, { recursive: true });
|
|
4581
|
+
const ext = artifact.form === "skill_md" ? ".md" : ".mjs";
|
|
4582
|
+
const publishedPath = join(publishDir, `${artifact.id}${ext}`);
|
|
4583
|
+
writeFileSync(publishedPath, readFileSync(artifact.draftPath, "utf8"), "utf8");
|
|
4584
|
+
return this.core.publishSkill(request, publishedPath, actorOf(agent));
|
|
4585
|
+
}
|
|
4586
|
+
/** List skill artifacts. */
|
|
4587
|
+
listSkillArtifacts(agent, parentExperienceId, status) {
|
|
4588
|
+
const filter = {};
|
|
4589
|
+
if (parentExperienceId !== "") filter.parentExperienceId = parentExperienceId;
|
|
4590
|
+
if (status !== "") filter.status = status;
|
|
4591
|
+
return this.core.listSkillArtifacts(filter);
|
|
4592
|
+
}
|
|
4593
|
+
/** Get a single skill artifact. */
|
|
4594
|
+
getSkillArtifact(agent, id) {
|
|
4595
|
+
return this.core.getSkillArtifact(id) ?? null;
|
|
4596
|
+
}
|
|
4597
|
+
/** Check if an experience is a skill conversion candidate. */
|
|
4598
|
+
isSkillCandidate(agent, experienceId) {
|
|
4599
|
+
return this.core.isSkillCandidate(experienceId);
|
|
4600
|
+
}
|
|
4601
|
+
/**
|
|
4602
|
+
* Generate skill content from an experience using LLM.
|
|
4603
|
+
*/
|
|
4604
|
+
async generateSkillContentWithLlm(agent, experience, form) {
|
|
4605
|
+
const header = agent.session.requestHeader()?.config;
|
|
4606
|
+
if (header === void 0) return "";
|
|
4607
|
+
const provider = header.provider;
|
|
4608
|
+
const model = header.model;
|
|
4609
|
+
if (provider === void 0 || model === void 0) return "";
|
|
4610
|
+
const isScript = form === "script_mjs";
|
|
4611
|
+
const systemPrompt = isScript ? `你是一个技能脚本生成器。根据给定的经验(gist、路径步骤、判断背景、限制条件),生成一个可直接执行的 Node.js (.mjs) 脚本。
|
|
4612
|
+
|
|
4613
|
+
要求:
|
|
4614
|
+
1. 脚本必须自包含,不依赖外部包(只用 Node.js 内置模块)
|
|
4615
|
+
2. 脚本顶部用注释说明用途和使用方法
|
|
4616
|
+
3. 脚本要有错误处理
|
|
4617
|
+
4. 脚本要跨平台兼容(Windows/macOS/Linux)
|
|
4618
|
+
5. 只输出脚本代码,不要其他解释` : `你是一个 DSH skill 文档生成器。根据给定的经验(gist、路径步骤、判断背景、限制条件),生成一个 DSH skill 格式的 Markdown 文档。
|
|
4619
|
+
|
|
4620
|
+
要求:
|
|
4621
|
+
1. 使用 DSH skill 标准格式(标题、描述、触发条件、步骤)
|
|
4622
|
+
2. 步骤要具体可操作
|
|
4623
|
+
3. 包含适用场景和不适用场景
|
|
4624
|
+
4. 只输出 Markdown 内容,不要其他解释`;
|
|
4625
|
+
const messages = [createUserMessage({
|
|
4626
|
+
content: [{
|
|
4627
|
+
type: "text",
|
|
4628
|
+
text: `经验摘要:${experience.gist}
|
|
4629
|
+
|
|
4630
|
+
路径步骤:
|
|
4631
|
+
${experience.path.map((s, i) => `${i + 1}. ${s.action}`).join("\n")}
|
|
4632
|
+
|
|
4633
|
+
判断背景:${experience.reasoning}
|
|
4634
|
+
|
|
4635
|
+
限制条件:
|
|
4636
|
+
${experience.limits.map((l) => `- ${l}`).join("\n")}
|
|
4637
|
+
|
|
4638
|
+
请生成${isScript ? "可执行脚本" : "skill 文档"}。`
|
|
4639
|
+
}],
|
|
4640
|
+
source: {
|
|
4641
|
+
kind: "plugin",
|
|
4642
|
+
plugin: "dsh-daoing-memory"
|
|
4643
|
+
}
|
|
4644
|
+
})];
|
|
4645
|
+
let text = "";
|
|
4646
|
+
for await (const chunk of this.ctx.llm.stream({
|
|
4647
|
+
provider,
|
|
4648
|
+
model,
|
|
4649
|
+
messages,
|
|
4650
|
+
system: systemPrompt,
|
|
4651
|
+
maxTokens: 4e3,
|
|
4652
|
+
sessionId: agent.session.id
|
|
4653
|
+
})) if (chunk.type === "text-delta") text += chunk.text;
|
|
4654
|
+
return text.trim();
|
|
4655
|
+
}
|
|
3344
4656
|
};
|
|
3345
4657
|
})();
|
|
3346
4658
|
//#endregion
|