dsh-mnemon 0.5.42 → 0.5.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/repair-legacy-session.mjs +18 -2
- package/lib/bundle.js +1 -1
- package/lib/client.js +587 -162
- package/lib/extension-sdk.js +39 -1
- package/lib/index.js +818 -170
- package/lib/{starter-resolution-B5mdKlvf.js → starter-resolution-LTLQsJPW.js} +7 -7
- package/lib/starter.js +1 -1
- package/lib/types/src/client/locales.d.ts +25 -0
- package/lib/types/src/host/protocol.d.ts +11 -0
- package/lib/types/src/sdk/index.d.ts +1 -0
- package/lib/types/src/sdk/projection-reservation.d.ts +3 -0
- package/locale/detail.en.md +7 -0
- package/locale/detail.zh.md +7 -0
- package/locale/en.json +3 -1
- package/locale/zh.json +2 -1
- package/package.json +9 -8
package/lib/index.js
CHANGED
|
@@ -6,8 +6,8 @@ import { Context } from "@deepseek-ai/cordis";
|
|
|
6
6
|
import z from "@deepseek-ai/schemastery";
|
|
7
7
|
import { basename, delimiter, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
8
8
|
import { resolveEmbedding, resolvePersistenceStrategy, resolveRecallQuality } from "dsh-mnemon-source-memory-spaces";
|
|
9
|
-
import { CATEGORIES, DEFAULT_EMBEDDING_ENDPOINT, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_PROTOCOL, EDGE_TYPES, MEMORY_PLACEMENT_CAPABILITIES, MEMORY_PROVIDER_REGISTRY_FILE, MEMORY_SPACES_DIRECTORY, MEMORY_SPACE_DATABASE_FILE, MEMORY_SPACE_ID_PATTERN, MEMORY_SPACE_NAME_MAX_LENGTH, MEMORY_SPACE_REGISTRY_FILE, MNEMON_ACTIVE_STORE_FILE, MNEMON_EMBEDDING_PROTOCOLS, SOURCES, parseMemorySpaceRegistry, summarizeMemoryProviderRegistry, summarizeMemorySpaceRegistry } from "dsh-mnemon-source-memory-spaces/contracts";
|
|
10
|
-
import { RUNTIME_ARCHIVE_DIRECTORY, RUNTIME_DIRECTORY, RUNTIME_ENTRY_DELIMITER, RUNTIME_ENTRY_MAX_BYTES, RUNTIME_LOCK_FILE, RUNTIME_LOCK_STALE_MS, RUNTIME_MEMORIES_FILE, RUNTIME_MEMORY_LIMITS, RUNTIME_MEMORY_LIMIT_MAX_BYTES, RUNTIME_MEMORY_VERSION, RUNTIME_PROJECTION_FILES, parseRuntimeBranches, runtimeBytes, runtimeProjection, summarizeRuntimeMemories } from "dsh-mnemon-source-runtime/contracts";
|
|
9
|
+
import { CATEGORIES, DEFAULT_EMBEDDING_ENDPOINT, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_PROTOCOL, EDGE_TYPES, EMBEDDING_CREDENTIAL_PREFIX, MEMORY_PLACEMENT_CAPABILITIES, MEMORY_PROVIDER_REGISTRY_FILE, MEMORY_SPACES_DIRECTORY, MEMORY_SPACE_DATABASE_FILE, MEMORY_SPACE_ID_PATTERN, MEMORY_SPACE_NAME_MAX_LENGTH, MEMORY_SPACE_REGISTRY_FILE, MNEMON_ACTIVE_STORE_FILE, MNEMON_EMBEDDING_PROTOCOLS, SOURCES, parseMemorySpaceRegistry, summarizeMemoryProviderRegistry, summarizeMemorySpaceRegistry } from "dsh-mnemon-source-memory-spaces/contracts";
|
|
10
|
+
import { RUNTIME_ARCHIVE_DIRECTORY, RUNTIME_DIRECTORY, RUNTIME_ENTRY_DELIMITER, RUNTIME_ENTRY_MAX_BYTES, RUNTIME_LOCK_FILE, RUNTIME_LOCK_STALE_MS, RUNTIME_MEMORIES_FILE, RUNTIME_MEMORY_LIMITS, RUNTIME_MEMORY_LIMIT_MAX_BYTES, RUNTIME_MEMORY_VERSION, RUNTIME_PROJECTION_FILES, assertRuntimePinnedBudget, parseRuntimeBranches, runtimeBytes, runtimeProjection, summarizeRuntimeMemories } from "dsh-mnemon-source-runtime/contracts";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import fs, { accessSync, chmodSync, closeSync, constants, copyFileSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { homedir, hostname } from "node:os";
|
|
@@ -23,6 +23,8 @@ import { Agent, request as request$1 } from "node:https";
|
|
|
23
23
|
import { connect } from "node:tls";
|
|
24
24
|
import { readFile, stat } from "node:fs/promises";
|
|
25
25
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
26
|
+
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
27
|
+
import * as SkillsContracts from "dsh-mnemon-source-skills/contracts";
|
|
26
28
|
import { z as z$1 } from "zod";
|
|
27
29
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
28
30
|
import { isVolatile } from "@deepseek-ai/cosmokit";
|
|
@@ -389,6 +391,9 @@ const DEFAULT_SKILLS_CONFIG = Object.freeze({
|
|
|
389
391
|
allowUpdate: true,
|
|
390
392
|
targetScope: "user",
|
|
391
393
|
autoBacktest: true,
|
|
394
|
+
autoAdopt: false,
|
|
395
|
+
autoAdoptDailyLimit: 2,
|
|
396
|
+
autoAdoptTimeZone: "local",
|
|
392
397
|
draftMaxTokens: 32768,
|
|
393
398
|
draftRetryMaxTokens: 65536,
|
|
394
399
|
hints: true,
|
|
@@ -483,6 +488,18 @@ const SkillsConfig = z.object({
|
|
|
483
488
|
"project"
|
|
484
489
|
]).default(DEFAULT_SKILLS_CONFIG.targetScope),
|
|
485
490
|
autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
|
|
491
|
+
autoAdopt: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoAdopt),
|
|
492
|
+
autoAdoptDailyLimit: z.number().step(1).min(0).max(100).default(DEFAULT_SKILLS_CONFIG.autoAdoptDailyLimit),
|
|
493
|
+
autoAdoptTimeZone: z.transform(z.string(), (value) => {
|
|
494
|
+
if (value === "local") return value;
|
|
495
|
+
if (!value || value.length > 100) throw new Error("skills.autoAdoptTimeZone must be an IANA time zone or local");
|
|
496
|
+
try {
|
|
497
|
+
new Intl.DateTimeFormat("en", { timeZone: value }).format(0);
|
|
498
|
+
} catch {
|
|
499
|
+
throw new Error("skills.autoAdoptTimeZone must be an IANA time zone or local");
|
|
500
|
+
}
|
|
501
|
+
return value;
|
|
502
|
+
}).default(DEFAULT_SKILLS_CONFIG.autoAdoptTimeZone),
|
|
486
503
|
draftMaxTokens: z.number().step(1).min(128).max(131072).default(DEFAULT_SKILLS_CONFIG.draftMaxTokens),
|
|
487
504
|
draftRetryMaxTokens: z.number().step(1).min(128).max(131072).default(DEFAULT_SKILLS_CONFIG.draftRetryMaxTokens),
|
|
488
505
|
hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
|
|
@@ -526,7 +543,9 @@ const MnemonEmbeddingSchema = z.object({
|
|
|
526
543
|
enabled: z.boolean().default(false),
|
|
527
544
|
endpoint: z.string().default(DEFAULT_EMBEDDING_ENDPOINT),
|
|
528
545
|
model: z.string().default(DEFAULT_EMBEDDING_MODEL),
|
|
529
|
-
apiKey: z.string().default(""),
|
|
546
|
+
apiKey: z.string().role("secret").default(""),
|
|
547
|
+
apiKeyEnv: z.string(),
|
|
548
|
+
apiKeyRef: z.string(),
|
|
530
549
|
protocol: z.union(MNEMON_EMBEDDING_PROTOCOLS).default(DEFAULT_EMBEDDING_PROTOCOL)
|
|
531
550
|
});
|
|
532
551
|
const RecallQualitySchema = z.object({
|
|
@@ -539,6 +558,7 @@ const RecallQualitySchema = z.object({
|
|
|
539
558
|
maxUnknownResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_UNKNOWN_RESULTS)
|
|
540
559
|
});
|
|
541
560
|
const RuntimeMemorySchema = z.object({
|
|
561
|
+
inheritToSubagents: z.boolean().default(false),
|
|
542
562
|
memoryLimitBytes: z.number().step(1).min(1).max(MAX_RUNTIME_MEMORY_LIMIT_BYTES).default(DEFAULT_RUNTIME_MEMORY_LIMIT_BYTES),
|
|
543
563
|
userLimitBytes: z.number().step(1).min(1).max(MAX_RUNTIME_MEMORY_LIMIT_BYTES).default(DEFAULT_RUNTIME_USER_LIMIT_BYTES),
|
|
544
564
|
maintenanceMaxTokens: z.number().step(1).min(1).max(MAX_RUNTIME_MAINTENANCE_MAX_TOKENS).default(DEFAULT_RUNTIME_MAINTENANCE_MAX_TOKENS)
|
|
@@ -595,6 +615,7 @@ const Config = z.object({
|
|
|
595
615
|
timeoutMs: z.number().step(1).min(100).max(12e4).default(DEFAULT_TIMEOUT_MS),
|
|
596
616
|
defaultRecallLimit: z.number().step(1).min(1).max(50).default(10),
|
|
597
617
|
runtimeMemory: RuntimeMemorySchema.default({
|
|
618
|
+
inheritToSubagents: false,
|
|
598
619
|
memoryLimitBytes: DEFAULT_RUNTIME_MEMORY_LIMIT_BYTES,
|
|
599
620
|
userLimitBytes: DEFAULT_RUNTIME_USER_LIMIT_BYTES,
|
|
600
621
|
maintenanceMaxTokens: DEFAULT_RUNTIME_MAINTENANCE_MAX_TOKENS
|
|
@@ -719,6 +740,8 @@ function resolveTaskAgentModel(value) {
|
|
|
719
740
|
};
|
|
720
741
|
}
|
|
721
742
|
function resolveRuntimeMemory(value) {
|
|
743
|
+
const inheritToSubagents = value?.inheritToSubagents ?? false;
|
|
744
|
+
if (typeof inheritToSubagents !== "boolean") throw new Error("dsh-mnemon: Runtime inheritToSubagents must be a boolean");
|
|
722
745
|
const memoryLimitBytes = value?.memoryLimitBytes ?? DEFAULT_RUNTIME_MEMORY_LIMIT_BYTES;
|
|
723
746
|
const userLimitBytes = value?.userLimitBytes ?? DEFAULT_RUNTIME_USER_LIMIT_BYTES;
|
|
724
747
|
const maintenanceMaxTokens = value?.maintenanceMaxTokens ?? 8192;
|
|
@@ -726,6 +749,7 @@ function resolveRuntimeMemory(value) {
|
|
|
726
749
|
if (!Number.isInteger(userLimitBytes) || userLimitBytes < 1 || userLimitBytes > MAX_RUNTIME_MEMORY_LIMIT_BYTES) throw new Error(`dsh-mnemon: Runtime USER.md limit must be an integer within 1..${MAX_RUNTIME_MEMORY_LIMIT_BYTES} bytes`);
|
|
727
750
|
if (!Number.isInteger(maintenanceMaxTokens) || maintenanceMaxTokens < 1 || maintenanceMaxTokens > 1e6) throw new Error(`dsh-mnemon: Runtime maintenance maxTokens must be an integer within 1..${MAX_RUNTIME_MAINTENANCE_MAX_TOKENS}`);
|
|
728
751
|
return {
|
|
752
|
+
inheritToSubagents,
|
|
729
753
|
memoryLimitBytes,
|
|
730
754
|
userLimitBytes,
|
|
731
755
|
maintenanceMaxTokens
|
|
@@ -2250,6 +2274,7 @@ function parseRuntime(value, limits = RUNTIME_MEMORY_LIMITS) {
|
|
|
2250
2274
|
"low"
|
|
2251
2275
|
].includes(String(entry.importance))) throw new Error("runtime memories.json contains an invalid entry");
|
|
2252
2276
|
if (typeof entry.created_at !== "string" || typeof entry.updated_at !== "string") throw new Error("runtime memories.json contains invalid timestamps");
|
|
2277
|
+
if (entry.pinned !== void 0 && typeof entry.pinned !== "boolean") throw new Error("runtime memories.json contains invalid pin state");
|
|
2253
2278
|
const content = entry.content.trim().replace(/\s+/gu, " ");
|
|
2254
2279
|
if (content === "" || content.includes("§") || Buffer.byteLength(content, "utf8") > RUNTIME_ENTRY_MAX_BYTES) throw new Error("runtime memories.json contains invalid content");
|
|
2255
2280
|
const branches = entry.target === "memory" ? parseRuntimeBranches(entry.branches) : void 0;
|
|
@@ -2260,10 +2285,12 @@ function parseRuntime(value, limits = RUNTIME_MEMORY_LIMITS) {
|
|
|
2260
2285
|
importance: entry.importance,
|
|
2261
2286
|
created_at: entry.created_at,
|
|
2262
2287
|
updated_at: entry.updated_at,
|
|
2288
|
+
...entry.pinned === void 0 ? {} : { pinned: entry.pinned },
|
|
2263
2289
|
...branches === void 0 ? {} : { branches },
|
|
2264
2290
|
...origin === void 0 ? {} : { origin }
|
|
2265
2291
|
};
|
|
2266
2292
|
});
|
|
2293
|
+
assertRuntimePinnedBudget(entries);
|
|
2267
2294
|
for (const target of ["user", "memory"]) {
|
|
2268
2295
|
const used = runtimeBytes(entries, target);
|
|
2269
2296
|
if (used > limits[target]) throw new Error(`runtime ${target} memory holds ${used} bytes and exceeds its ${limits[target]} byte limit`);
|
|
@@ -2476,6 +2503,7 @@ function archiveRuntimeOverflow(stagedRuntime, entries) {
|
|
|
2476
2503
|
}
|
|
2477
2504
|
/** Retain the same hot set on both machines, without asking a model to rewrite evidence. */
|
|
2478
2505
|
function fitMergedRuntime(entries, limits) {
|
|
2506
|
+
if (runtimeBytes(entries.filter((entry) => entry.pinned === true), "memory") > limits.memory) throw new Error("Pinned runtime entries exceed the merge capacity. Unpin entries or increase the capacity; no data was changed.");
|
|
2479
2507
|
if (runtimeBytes(entries, "memory") <= limits.memory) return {
|
|
2480
2508
|
entries,
|
|
2481
2509
|
archived: []
|
|
@@ -2486,7 +2514,7 @@ function fitMergedRuntime(entries, limits) {
|
|
|
2486
2514
|
low: 0
|
|
2487
2515
|
};
|
|
2488
2516
|
const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
2489
|
-
const ranked = entries.filter((entry) => entry.target === "memory").sort((left, right) => importance[right.importance] - importance[left.importance] || compare(right.updated_at, left.updated_at) || compare(entryKey(left), entryKey(right)));
|
|
2517
|
+
const ranked = entries.filter((entry) => entry.target === "memory").sort((left, right) => Number(right.pinned === true) - Number(left.pinned === true) || importance[right.importance] - importance[left.importance] || compare(right.updated_at, left.updated_at) || compare(entryKey(left), entryKey(right)));
|
|
2490
2518
|
const kept = [];
|
|
2491
2519
|
const archived = [];
|
|
2492
2520
|
for (const entry of ranked) if (runtimeBytes([...kept, entry], "memory") <= limits.memory) kept.push(entry);
|
|
@@ -2835,7 +2863,14 @@ function mergeRuntime(root, pack, runtimeLimits, options = {}) {
|
|
|
2835
2863
|
}
|
|
2836
2864
|
for (const tombstone of hidden) if (!revived.includes(tombstone)) revived.push(tombstone);
|
|
2837
2865
|
}
|
|
2838
|
-
if (keys.has(key))
|
|
2866
|
+
if (keys.has(key)) {
|
|
2867
|
+
const existing = entries.find((candidate) => entryKey(candidate) === key);
|
|
2868
|
+
if (entry.pinned !== void 0 && (existing.pinned === void 0 || entry.updated_at > existing.updated_at)) {
|
|
2869
|
+
existing.pinned = entry.pinned;
|
|
2870
|
+
if (entry.updated_at > existing.updated_at) existing.updated_at = entry.updated_at;
|
|
2871
|
+
}
|
|
2872
|
+
continue;
|
|
2873
|
+
}
|
|
2839
2874
|
keys.add(key);
|
|
2840
2875
|
entries.push(entry);
|
|
2841
2876
|
incomingKeys.add(key);
|
|
@@ -3560,6 +3595,44 @@ var MnemonReviewLedger = class {
|
|
|
3560
3595
|
}
|
|
3561
3596
|
};
|
|
3562
3597
|
//#endregion
|
|
3598
|
+
//#region src/host/disposable-directory.ts
|
|
3599
|
+
function makeWritable(path) {
|
|
3600
|
+
let entry;
|
|
3601
|
+
try {
|
|
3602
|
+
entry = lstatSync(path);
|
|
3603
|
+
} catch (error) {
|
|
3604
|
+
if (error.code === "ENOENT") return;
|
|
3605
|
+
throw error;
|
|
3606
|
+
}
|
|
3607
|
+
if (entry.isSymbolicLink()) return;
|
|
3608
|
+
if ((entry.mode & 128) === 0) chmodSync(path, entry.mode | 128);
|
|
3609
|
+
if (entry.isDirectory()) for (const name of readdirSync(path)) makeWritable(join(path, name));
|
|
3610
|
+
}
|
|
3611
|
+
/** Remove only a caller-owned disposable tree, including read-only Git packs on Windows. */
|
|
3612
|
+
function removeDisposableDirectory(path) {
|
|
3613
|
+
try {
|
|
3614
|
+
rmSync(path, {
|
|
3615
|
+
recursive: true,
|
|
3616
|
+
force: true
|
|
3617
|
+
});
|
|
3618
|
+
} catch (error) {
|
|
3619
|
+
const code = error.code;
|
|
3620
|
+
if (process.platform !== "win32" || ![
|
|
3621
|
+
"EPERM",
|
|
3622
|
+
"EACCES",
|
|
3623
|
+
"ENOTEMPTY",
|
|
3624
|
+
"EBUSY"
|
|
3625
|
+
].includes(code ?? "")) throw error;
|
|
3626
|
+
makeWritable(path);
|
|
3627
|
+
rmSync(path, {
|
|
3628
|
+
recursive: true,
|
|
3629
|
+
force: true,
|
|
3630
|
+
maxRetries: 3,
|
|
3631
|
+
retryDelay: 100
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
//#endregion
|
|
3563
3636
|
//#region src/host/github-transport.ts
|
|
3564
3637
|
/**
|
|
3565
3638
|
* How GitHub sign-in reaches GitHub.
|
|
@@ -4598,10 +4671,7 @@ var MnemonSyncSettingsStore = class {
|
|
|
4598
4671
|
};
|
|
4599
4672
|
}
|
|
4600
4673
|
clearMirror() {
|
|
4601
|
-
|
|
4602
|
-
recursive: true,
|
|
4603
|
-
force: true
|
|
4604
|
-
});
|
|
4674
|
+
removeDisposableDirectory(this.mirror());
|
|
4605
4675
|
}
|
|
4606
4676
|
};
|
|
4607
4677
|
//#endregion
|
|
@@ -5983,16 +6053,10 @@ var MnemonGitSync = class {
|
|
|
5983
6053
|
cwd: mirror,
|
|
5984
6054
|
signal
|
|
5985
6055
|
});
|
|
5986
|
-
if (origin.exitCode !== 0 || origin.stdout.trim() !== settings.repoUrl)
|
|
5987
|
-
recursive: true,
|
|
5988
|
-
force: true
|
|
5989
|
-
});
|
|
6056
|
+
if (origin.exitCode !== 0 || origin.stdout.trim() !== settings.repoUrl) removeDisposableDirectory(mirror);
|
|
5990
6057
|
}
|
|
5991
6058
|
if (!existsSync(join(mirror, ".git"))) {
|
|
5992
|
-
|
|
5993
|
-
recursive: true,
|
|
5994
|
-
force: true
|
|
5995
|
-
});
|
|
6059
|
+
removeDisposableDirectory(mirror);
|
|
5996
6060
|
const initialized = await this.git([
|
|
5997
6061
|
"init",
|
|
5998
6062
|
"--quiet",
|
|
@@ -6119,10 +6183,7 @@ var MnemonGitSync = class {
|
|
|
6119
6183
|
const root = this.payloadRoot(subdir);
|
|
6120
6184
|
const mirror = resolve(this.store.mirror());
|
|
6121
6185
|
if (root === mirror || !root.startsWith(mirror + (process.platform === "win32" ? "\\" : "/"))) throw new Error("sync directory escapes the mirror");
|
|
6122
|
-
|
|
6123
|
-
recursive: true,
|
|
6124
|
-
force: true
|
|
6125
|
-
});
|
|
6186
|
+
removeDisposableDirectory(root);
|
|
6126
6187
|
for (const [path, bytes] of Object.entries(entries)) {
|
|
6127
6188
|
const file = resolve(root, path);
|
|
6128
6189
|
mkdirSync(join(file, ".."), {
|
|
@@ -9456,7 +9517,7 @@ const WRITE_TOOL_FALLBACK_ACTION = {
|
|
|
9456
9517
|
};
|
|
9457
9518
|
function reviewReceipt(receipt) {
|
|
9458
9519
|
if (!mutationResultCommitted(receipt.value)) return void 0;
|
|
9459
|
-
const value = object$
|
|
9520
|
+
const value = object$7(receipt.value);
|
|
9460
9521
|
const args = optionalObject(receipt.arguments);
|
|
9461
9522
|
const document = optionalObject(value.document);
|
|
9462
9523
|
return {
|
|
@@ -9467,7 +9528,7 @@ function reviewReceipt(receipt) {
|
|
|
9467
9528
|
...typeof value.revision === "string" ? { revision: value.revision } : {}
|
|
9468
9529
|
};
|
|
9469
9530
|
}
|
|
9470
|
-
function object$
|
|
9531
|
+
function object$7(value) {
|
|
9471
9532
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("memory subagent returned an invalid structured result");
|
|
9472
9533
|
return value;
|
|
9473
9534
|
}
|
|
@@ -9672,7 +9733,11 @@ function runtimeRouteChunks(entries) {
|
|
|
9672
9733
|
function compactedBudget(plan) {
|
|
9673
9734
|
const pendingBytes = plan.pending === void 0 ? 0 : Buffer.byteLength(plan.pending.content, "utf8");
|
|
9674
9735
|
const separatorBytes = plan.pending === void 0 || plan.entries.length === 0 ? 0 : Buffer.byteLength(RUNTIME_ENTRY_DELIMITER, "utf8");
|
|
9675
|
-
|
|
9736
|
+
const pinned = plan.entries.filter((entry) => entry.pinned === true);
|
|
9737
|
+
const pinnedBytes = Buffer.byteLength(pinned.map((entry) => entry.content).join(RUNTIME_ENTRY_DELIMITER), "utf8");
|
|
9738
|
+
const available = plan.limit - pendingBytes - separatorBytes;
|
|
9739
|
+
if ((pinned.length > 0 || plan.pending?.pinned === true) && pinnedBytes > available) throw new Error("Pinned runtime entries leave no capacity for this write. Unpin entries or increase the capacity; no archive writes were attempted.");
|
|
9740
|
+
return Math.max(pinnedBytes, Math.max(0, Math.floor(plan.limit * .7) - pendingBytes - separatorBytes));
|
|
9676
9741
|
}
|
|
9677
9742
|
function runtimeMigrationSources(revision, entries) {
|
|
9678
9743
|
return entries.map((entry, offset) => ({
|
|
@@ -9703,7 +9768,7 @@ async function runtimeArchiveSource(runtimeSource, graph, scope, view, generatio
|
|
|
9703
9768
|
return {
|
|
9704
9769
|
source,
|
|
9705
9770
|
cleanup,
|
|
9706
|
-
memoryBodyIds: new Set(strings(object$
|
|
9771
|
+
memoryBodyIds: new Set(strings(object$7(grant.value).memoryBodyIds)),
|
|
9707
9772
|
writeScope: {
|
|
9708
9773
|
viewId: view.id,
|
|
9709
9774
|
grant
|
|
@@ -9721,10 +9786,11 @@ async function archiveRuntimeLocally(counters, context, request, plan, reason, s
|
|
|
9721
9786
|
if (plan.localArchive !== true) throw new Error(`runtime memory archival found no Memory Space that can take the archive (${reason}), and this Runtime Source cannot archive locally; pending write was not committed and existing runtime entries are unchanged; update dsh-mnemon-source-runtime, activate a supported Memory Space, or increase runtimeMemory.memoryLimitBytes`);
|
|
9722
9787
|
context.assertWritable?.();
|
|
9723
9788
|
const runId = `host-${randomUUID()}`;
|
|
9724
|
-
const compacted = plan.entries.map(({ content, importance, branches }) => ({
|
|
9789
|
+
const compacted = plan.entries.map(({ content, importance, branches, pinned }) => ({
|
|
9725
9790
|
content,
|
|
9726
9791
|
importance,
|
|
9727
|
-
...branches === void 0 ? {} : { branches }
|
|
9792
|
+
...branches === void 0 ? {} : { branches },
|
|
9793
|
+
...pinned === void 0 ? {} : { pinned }
|
|
9728
9794
|
}));
|
|
9729
9795
|
const mutation = await context.runtime.mutateResult("compact-and-mutate", {
|
|
9730
9796
|
revision: plan.revision,
|
|
@@ -9767,7 +9833,9 @@ async function runtimeLocked(counters, context, request, signal) {
|
|
|
9767
9833
|
if (context.expectedRevision !== void 0 && context.expectedRevision !== plan.revision) throw new Error("Runtime source revision conflict before capacity maintenance");
|
|
9768
9834
|
if (!plan.requiresMaintenance) return context.commit();
|
|
9769
9835
|
if (plan.entries.length === 0) throw new Error("runtime memory capacity was exceeded without entries available for maintenance");
|
|
9836
|
+
compactedBudget(plan);
|
|
9770
9837
|
if (request.target === "user") return compactUserAndCommit(context, request, plan, signal);
|
|
9838
|
+
const archiveEntries = plan.entries.filter((entry) => entry.pinned !== true);
|
|
9771
9839
|
const archive = await context.memorySpaces();
|
|
9772
9840
|
if (archive === void 0) return archiveRuntimeLocally(counters, context, request, plan, "no Memory Space takes automatic writes here", signal);
|
|
9773
9841
|
const memoryService = archive.source;
|
|
@@ -9802,11 +9870,11 @@ async function runtimeLocked(counters, context, request, signal) {
|
|
|
9802
9870
|
let provider = "host";
|
|
9803
9871
|
let runId = `host-${randomUUID()}`;
|
|
9804
9872
|
let summary = "Routed every entry to the only eligible Memory Space without model work.";
|
|
9805
|
-
if (eligibleBodies.length === 1) for (const index of
|
|
9873
|
+
if (eligibleBodies.length === 1) for (const index of archiveEntries.keys()) routed.set(index + 1, eligibleBodies[0].id);
|
|
9806
9874
|
else {
|
|
9807
9875
|
const summaries = [];
|
|
9808
9876
|
const fallbackBody = eligibleBodies.find((body) => body.mnemonDefault) ?? eligibleBodies[0];
|
|
9809
|
-
const chunks = runtimeRouteChunks(
|
|
9877
|
+
const chunks = runtimeRouteChunks(archiveEntries);
|
|
9810
9878
|
for (const [chunkIndex, chunk] of chunks.entries()) {
|
|
9811
9879
|
const prompt = `Route this bounded MEMORY.md archive batch now. The host retains and writes the exact source content; these excerpts exist only for destination selection.
|
|
9812
9880
|
|
|
@@ -9825,13 +9893,13 @@ ${chunk.context}
|
|
|
9825
9893
|
provider = delegated.provider;
|
|
9826
9894
|
runId = delegated.runId;
|
|
9827
9895
|
}
|
|
9828
|
-
const value = object$
|
|
9896
|
+
const value = object$7(delegated.result.structured);
|
|
9829
9897
|
if (value.action !== "planned") throw new Error(typeof value.summary === "string" && value.summary !== "" ? value.summary : "runtime memory archival routing failed");
|
|
9830
9898
|
if (!Array.isArray(value.routes) || value.routes.length === 0) throw new Error("runtime memory migration returned no routes");
|
|
9831
9899
|
const allowedIndexes = new Set(chunk.indexes);
|
|
9832
9900
|
const proposed = /* @__PURE__ */ new Map();
|
|
9833
9901
|
for (const candidate of value.routes) {
|
|
9834
|
-
const route = object$
|
|
9902
|
+
const route = object$7(candidate);
|
|
9835
9903
|
const memoryBodyId = typeof route.memoryBodyId === "string" ? route.memoryBodyId.trim() : "";
|
|
9836
9904
|
if (memoryBodyId === "" || !eligibleById.has(memoryBodyId)) throw new Error(`runtime memory migration selected an invalid Memory Space: ${memoryBodyId || "(empty)"}`);
|
|
9837
9905
|
if (!Array.isArray(route.sourceIndexes) || route.sourceIndexes.length === 0) throw new Error("runtime memory migration route must contain source indexes");
|
|
@@ -9851,11 +9919,12 @@ ${chunk.context}
|
|
|
9851
9919
|
}
|
|
9852
9920
|
summary = summaries.join(" ");
|
|
9853
9921
|
}
|
|
9854
|
-
if (routed.size !==
|
|
9855
|
-
const compactedEntries = plan.entries.map(({ content, importance, branches }) => ({
|
|
9922
|
+
if (routed.size !== archiveEntries.length) throw new Error("runtime memory migration omitted committed archive sources");
|
|
9923
|
+
const compactedEntries = plan.entries.map(({ content, importance, branches, pinned }) => ({
|
|
9856
9924
|
content,
|
|
9857
9925
|
importance,
|
|
9858
|
-
...branches === void 0 ? {} : { branches }
|
|
9926
|
+
...branches === void 0 ? {} : { branches },
|
|
9927
|
+
...pinned === void 0 ? {} : { pinned }
|
|
9859
9928
|
}));
|
|
9860
9929
|
if ((await runtimeMemory.read("maintenance-plan", {
|
|
9861
9930
|
...request,
|
|
@@ -9865,9 +9934,9 @@ ${chunk.context}
|
|
|
9865
9934
|
for (const memoryBodyId of new Set(routed.values())) if (!destinations.items.some((body) => body.id === memoryBodyId && eligible(body))) throw new Error(`runtime archive Memory Space ${memoryBodyId} is no longer eligible; no archive writes were attempted`);
|
|
9866
9935
|
signal.throwIfAborted();
|
|
9867
9936
|
context.assertWritable?.();
|
|
9868
|
-
const sources = runtimeMigrationSources(plan.revision,
|
|
9937
|
+
const sources = runtimeMigrationSources(plan.revision, archiveEntries);
|
|
9869
9938
|
const requests = sources.map((source) => {
|
|
9870
|
-
const entry =
|
|
9939
|
+
const entry = archiveEntries[source.index - 1];
|
|
9871
9940
|
return {
|
|
9872
9941
|
content: entry.content,
|
|
9873
9942
|
category: "context",
|
|
@@ -9910,7 +9979,7 @@ ${chunk.context}
|
|
|
9910
9979
|
archiveResults.set(source.index, result);
|
|
9911
9980
|
}
|
|
9912
9981
|
if (written.length !== batch.length) throw new Error("runtime archive batch did not return one receipt per source entry");
|
|
9913
|
-
for (const source of batch) verified.set(source.index, await archiveRuntimeEntry(memoryService, memoryBodyId,
|
|
9982
|
+
for (const source of batch) verified.set(source.index, await archiveRuntimeEntry(memoryService, memoryBodyId, archiveEntries[source.index - 1], archiveResults.get(source.index), signal));
|
|
9914
9983
|
}
|
|
9915
9984
|
const lineage = [];
|
|
9916
9985
|
const memoryBodyIds = /* @__PURE__ */ new Set();
|
|
@@ -9977,16 +10046,18 @@ ${chunk.context}
|
|
|
9977
10046
|
async function compactUserAndCommit(context, request, plan, signal) {
|
|
9978
10047
|
const runtimeMemory = context.runtime;
|
|
9979
10048
|
const budget = compactedBudget(plan);
|
|
10049
|
+
const pinned = plan.entries.filter((entry) => entry.pinned === true);
|
|
10050
|
+
const mutable = plan.entries.filter((entry) => entry.pinned !== true);
|
|
9980
10051
|
const prompt = `Run local USER.md compaction now.
|
|
9981
10052
|
Pending mutation (uncommitted; do not include in compaction):
|
|
9982
10053
|
${pendingMutationContext(plan)}
|
|
9983
10054
|
|
|
9984
|
-
${runtimeSnapshotContext("user",
|
|
10055
|
+
${runtimeSnapshotContext("user", mutable)}`;
|
|
9985
10056
|
const { provider, runId, result } = await context.model("compaction", "Consolidate local user profile", prompt, USER_COMPACTION_SCHEMA, USER_COMPACTION_PERSONA);
|
|
9986
|
-
const value = object$
|
|
10057
|
+
const value = object$7(result.structured);
|
|
9987
10058
|
if (value.action !== "compacted") throw new Error(typeof value.summary === "string" && value.summary !== "" ? value.summary : "USER.md compaction failed");
|
|
9988
10059
|
const compactedEntries = Array.isArray(value.compactedEntries) ? value.compactedEntries.map((entry) => {
|
|
9989
|
-
const item = object$
|
|
10060
|
+
const item = object$7(entry);
|
|
9990
10061
|
if (typeof item.content !== "string" || ![
|
|
9991
10062
|
"critical",
|
|
9992
10063
|
"normal",
|
|
@@ -10010,17 +10081,21 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
|
|
|
10010
10081
|
if (entry.sourceIndexes.length === 0) throw new Error("USER.md compaction returned an entry without a source");
|
|
10011
10082
|
let requiredRank = 0;
|
|
10012
10083
|
for (const index of entry.sourceIndexes) {
|
|
10013
|
-
if (index < 1 || index >
|
|
10084
|
+
if (index < 1 || index > mutable.length || seen.has(index)) throw new Error("USER.md compaction source coverage is invalid");
|
|
10014
10085
|
seen.add(index);
|
|
10015
|
-
requiredRank = Math.max(requiredRank, importanceRank[
|
|
10086
|
+
requiredRank = Math.max(requiredRank, importanceRank[mutable[index - 1].importance]);
|
|
10016
10087
|
}
|
|
10017
10088
|
if (importanceRank[entry.importance] < requiredRank) throw new Error("USER.md compaction lowered source importance");
|
|
10018
10089
|
}
|
|
10019
|
-
if (seen.size !==
|
|
10020
|
-
const candidates =
|
|
10090
|
+
if (seen.size !== mutable.length) throw new Error("USER.md compaction omitted committed entries");
|
|
10091
|
+
const candidates = [...pinned.map((entry) => ({
|
|
10092
|
+
content: entry.content,
|
|
10093
|
+
importance: entry.importance,
|
|
10094
|
+
pinned: true
|
|
10095
|
+
})), ...compactedEntries.map(({ content, importance }) => ({
|
|
10021
10096
|
content,
|
|
10022
10097
|
importance
|
|
10023
|
-
}));
|
|
10098
|
+
}))];
|
|
10024
10099
|
const candidateBytes = Buffer.byteLength(candidates.map((entry) => entry.content.trim().replace(/\s+/gu, " ")).join(RUNTIME_ENTRY_DELIMITER), "utf8");
|
|
10025
10100
|
if (candidateBytes > budget) throw new Error(`USER.md compaction did not fit the host budget (${candidateBytes} > ${budget} bytes)`);
|
|
10026
10101
|
signal.throwIfAborted();
|
|
@@ -10160,7 +10235,7 @@ async function archiveDocumentLocked(host, parent, id, signal) {
|
|
|
10160
10235
|
const cleanup = graph.source("memory-spaces", agentScope(parent, graph.config)).forInstance(identity.sourceInstanceKey).forGeneration(lease.generation);
|
|
10161
10236
|
const grant = authority?.context.view.readGrants.find((grant) => grant.sourceInstanceKey === identity.sourceInstanceKey && grant.schema === "dsh-mnemon.memory-spaces/v1");
|
|
10162
10237
|
if (authority !== void 0 && grant === void 0) throw new Error("document archive requires a Memory Space namespace grant");
|
|
10163
|
-
const allowed = grant === void 0 ? void 0 : new Set(strings(object$
|
|
10238
|
+
const allowed = grant === void 0 ? void 0 : new Set(strings(object$7(grant.value).memoryBodyIds));
|
|
10164
10239
|
const directory = await memoryService.read("body-directory", null, signal);
|
|
10165
10240
|
const indexable = (body) => archivableSpace(directory, body);
|
|
10166
10241
|
const eligible = (body) => indexable(body) && (allowed === void 0 || allowed.has(body.id));
|
|
@@ -10170,7 +10245,7 @@ async function archiveDocumentLocked(host, parent, id, signal) {
|
|
|
10170
10245
|
throw new Error("document archive requires an existing active Memory Space with exact writes and safe forget");
|
|
10171
10246
|
}
|
|
10172
10247
|
const { provider, runId, result } = await host.delegate(parent, "document-archive", "Plan managed document archive", documentArchivePrompt(document, source, bodies), [], DOCUMENT_ARCHIVE_SCHEMA, signal, "spawn", DOCUMENT_ARCHIVE_PERSONA);
|
|
10173
|
-
const value = object$
|
|
10248
|
+
const value = object$7(result.structured);
|
|
10174
10249
|
const summary = typeof value.summary === "string" ? value.summary.trim() : "";
|
|
10175
10250
|
if (value.action !== "planned") throw new Error(summary || "document archive planning failed");
|
|
10176
10251
|
if (summary === "" || summary.length > 1e3) throw new Error("document archive summary must contain 1–1000 characters");
|
|
@@ -10357,9 +10432,16 @@ const ACTION_TOOLS = namedBindings("action");
|
|
|
10357
10432
|
const NAMED_SOURCE_TYPES = [.../* @__PURE__ */ new Set([...Object.keys(ROUTE_TOOLS), ...Object.keys(ACTION_TOOLS)])];
|
|
10358
10433
|
/** Use a short availability list only where the Host has the matching named binding.
|
|
10359
10434
|
* Unbound external operations retain exact ids and schemas in the generic envelope. */
|
|
10360
|
-
function modelMemoryWake(graph, turn) {
|
|
10435
|
+
function modelMemoryWake(graph, turn, includeRuntime = true) {
|
|
10361
10436
|
const generation = graph.memoryComposition.generation(turn.view.runtimeGeneration);
|
|
10362
10437
|
if (generation === void 0) throw new Error("Cannot present an unpinned Memory generation");
|
|
10438
|
+
if (!includeRuntime) {
|
|
10439
|
+
const runtimeSources = new Set(generation.sourceInstances().filter((source) => source.sourceTypeId === "runtime").map((source) => source.sourceInstanceKey));
|
|
10440
|
+
return createMemoryWake({
|
|
10441
|
+
...turn.view,
|
|
10442
|
+
projection: turn.view.projection.filter((fragment) => !runtimeSources.has(fragment.sourceInstanceKey))
|
|
10443
|
+
}, memoryWakeBindings(generation, turn.view));
|
|
10444
|
+
}
|
|
10363
10445
|
return graph.composableTurns.memoryWake(turn.view.id, memoryWakeBindings(generation, turn.view));
|
|
10364
10446
|
}
|
|
10365
10447
|
function presentMemoryWake(generation, view) {
|
|
@@ -10466,6 +10548,23 @@ function openAgentTurn(agent) {
|
|
|
10466
10548
|
}
|
|
10467
10549
|
return open;
|
|
10468
10550
|
}
|
|
10551
|
+
/** PTC dispatch records belong to a real open step, not just an open turn. */
|
|
10552
|
+
function openAgentStep(agent) {
|
|
10553
|
+
let turn;
|
|
10554
|
+
let open;
|
|
10555
|
+
for (const event of agent.session.snapshotEvents()) if (event.type === "turn/start") {
|
|
10556
|
+
turn = typeof event.data.turn === "number" ? event.data.turn : void 0;
|
|
10557
|
+
open = void 0;
|
|
10558
|
+
} else if (event.type === "turn/end" && event.data.turn === turn) {
|
|
10559
|
+
turn = void 0;
|
|
10560
|
+
open = void 0;
|
|
10561
|
+
} else if (event.type === "step/start" && turn !== void 0 && event.data.turn === turn && typeof event.data.step === "number") open = {
|
|
10562
|
+
turn,
|
|
10563
|
+
step: event.data.step
|
|
10564
|
+
};
|
|
10565
|
+
else if (event.type === "step/end" && open?.turn === event.data.turn && open?.step === event.data.step) open = void 0;
|
|
10566
|
+
return open;
|
|
10567
|
+
}
|
|
10469
10568
|
/**
|
|
10470
10569
|
* One Agent owns each turn pin. A child additionally retains its delegation
|
|
10471
10570
|
* across parent completion, collection, and runtime swaps until it is disposed.
|
|
@@ -10485,17 +10584,23 @@ var AgentMemoryTurn = class {
|
|
|
10485
10584
|
lastInspection;
|
|
10486
10585
|
lastWorkspace;
|
|
10487
10586
|
releaseDelegation;
|
|
10587
|
+
includeRuntime;
|
|
10488
10588
|
constructor(agent, runtime, delegation, independent = false) {
|
|
10489
10589
|
this.agent = agent;
|
|
10490
10590
|
this.runtime = runtime;
|
|
10491
10591
|
this.delegation = delegation;
|
|
10492
10592
|
this.independent = independent;
|
|
10593
|
+
this.includeRuntime = delegation === void 0 || runtime.config.runtimeMemory.inheritToSubagents;
|
|
10493
10594
|
if (delegation === void 0) return;
|
|
10494
10595
|
this.releaseDelegation = runtime.executions.retain(agent, delegation);
|
|
10495
10596
|
}
|
|
10496
10597
|
get current() {
|
|
10497
10598
|
return this.pinned;
|
|
10498
10599
|
}
|
|
10600
|
+
modelWake() {
|
|
10601
|
+
const pinned = this.pinned;
|
|
10602
|
+
return pinned === void 0 ? void 0 : modelMemoryWake(pinned.graph, pinned.context, this.includeRuntime);
|
|
10603
|
+
}
|
|
10499
10604
|
/** Why the current turn has no View, when memory could not be composed for it. */
|
|
10500
10605
|
get unavailableReason() {
|
|
10501
10606
|
return this.unavailable?.reason;
|
|
@@ -10586,7 +10691,7 @@ var AgentMemoryTurn = class {
|
|
|
10586
10691
|
try {
|
|
10587
10692
|
if (this.closed || this.generation !== generation) throw new Error("memory turn ended during View preparation");
|
|
10588
10693
|
signal?.throwIfAborted();
|
|
10589
|
-
const inspection = inspectMemoryView(graph.memoryComposition.generation(context.view.runtimeGeneration), context.view, "active", turn, modelMemoryWake(graph, context).text);
|
|
10694
|
+
const inspection = inspectMemoryView(graph.memoryComposition.generation(context.view.runtimeGeneration), context.view, "active", turn, modelMemoryWake(graph, context, this.includeRuntime).text);
|
|
10590
10695
|
this.pinned = {
|
|
10591
10696
|
turn,
|
|
10592
10697
|
graph,
|
|
@@ -10630,7 +10735,7 @@ async function executeHostTool(tools, input) {
|
|
|
10630
10735
|
//#region src/host/review-admission.ts
|
|
10631
10736
|
const RESULT_TOOL = "mnemon_subagent_result";
|
|
10632
10737
|
const CHECK_TIMEOUT_MS = 5e3;
|
|
10633
|
-
/** An in-
|
|
10738
|
+
/** An in-step no-op challenge through the mandatory review completion channel.
|
|
10634
10739
|
* Inventory alone cannot see execution-time guards. No errors are classified as
|
|
10635
10740
|
* permission failures, and an unavailable checkpoint is never a permanent ban.
|
|
10636
10741
|
*/
|
|
@@ -10656,7 +10761,7 @@ var AutomaticReviewAdmission = class {
|
|
|
10656
10761
|
async check(agent, signal) {
|
|
10657
10762
|
signal.throwIfAborted();
|
|
10658
10763
|
const tools = agent.ctx.tools;
|
|
10659
|
-
if (this.disposed || tools?.execute === void 0 || this.draining.has(agent) ||
|
|
10764
|
+
if (this.disposed || tools?.execute === void 0 || this.draining.has(agent) || openAgentStep(agent) === void 0) return false;
|
|
10660
10765
|
const controller = new AbortController();
|
|
10661
10766
|
this.controllers.add(controller);
|
|
10662
10767
|
const combined = AbortSignal.any([signal, controller.signal]);
|
|
@@ -11069,7 +11174,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
11069
11174
|
if (!isSubagent(execution.agent)) throw new Error("Mnemon subagent result tools are restricted to delegated children");
|
|
11070
11175
|
execution.signal.throwIfAborted();
|
|
11071
11176
|
assertDshOutputValue(RESULT_TOOL_INPUT_SCHEMA, args);
|
|
11072
|
-
const request = object$
|
|
11177
|
+
const request = object$7(args);
|
|
11073
11178
|
const submit = this.resultRequests.get(String(request.requestId));
|
|
11074
11179
|
if (this.disposed || submit === void 0) throw new Error("Mnemon subagent result request is unknown or no longer active");
|
|
11075
11180
|
return submit(request.result, execution);
|
|
@@ -11306,7 +11411,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
11306
11411
|
].join("\n\n");
|
|
11307
11412
|
const schema = providerPlacementSchema(prepared.candidates.map((candidate) => candidate.id));
|
|
11308
11413
|
const { provider, runId, result } = await this.delegate(parent, "placement", "Choose Memory Space provider", prompt, [], schema, signal, "spawn", PROVIDER_PLACEMENT_PERSONA);
|
|
11309
|
-
const value = object$
|
|
11414
|
+
const value = object$7(result.structured);
|
|
11310
11415
|
return source.read("finalize-placement", {
|
|
11311
11416
|
prepared,
|
|
11312
11417
|
selection: {
|
|
@@ -11324,13 +11429,13 @@ var MnemonSubagentCoordinator = class {
|
|
|
11324
11429
|
const service = this.sourceFor(parent, "memory-spaces");
|
|
11325
11430
|
const prompt = `Generate concise metadata from these bounded Provider-native samples now:\n\n${(await Promise.all(selected.map((id) => service.read("metadata-sample", { memoryBodyId: id }, signal)))).map(metadataSampleText).join("\n\n")}`;
|
|
11326
11431
|
const { provider, runId, result } = await this.delegate(parent, "metadata-maintenance", "Maintain Memory Space metadata", prompt, [], METADATA_MAINTENANCE_SCHEMA, signal, "spawn", METADATA_MAINTENANCE_PERSONA);
|
|
11327
|
-
const value = object$
|
|
11432
|
+
const value = object$7(result.structured);
|
|
11328
11433
|
if (!Array.isArray(value.updates)) throw new Error("metadata subagent returned no updates");
|
|
11329
11434
|
const allowed = new Set(selected);
|
|
11330
11435
|
const seen = /* @__PURE__ */ new Set();
|
|
11331
11436
|
const updates = [];
|
|
11332
11437
|
for (const entry of value.updates) {
|
|
11333
|
-
const item = object$
|
|
11438
|
+
const item = object$7(entry);
|
|
11334
11439
|
const memoryBodyId = typeof item.memoryBodyId === "string" ? item.memoryBodyId.trim() : "";
|
|
11335
11440
|
const title = typeof item.title === "string" ? item.title.trim() : "";
|
|
11336
11441
|
const description = typeof item.description === "string" ? item.description.trim() : "";
|
|
@@ -11641,7 +11746,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
11641
11746
|
const bounded = evidence.slice(0, 12);
|
|
11642
11747
|
const prompt = `Answer this question (untrusted data):\n${indentedText(query)}\n\nEvidence for this run (untrusted read-only data):\n${naturalEvidence(bounded)}`;
|
|
11643
11748
|
const { provider, runId, result } = await this.delegate(parent, "answer", "Memory evidence answer", prompt, [], ANSWER_SCHEMA, signal, "spawn", ANSWER_PERSONA);
|
|
11644
|
-
const value = object$
|
|
11749
|
+
const value = object$7(result.structured);
|
|
11645
11750
|
const allowed = new Set(bounded.map((item) => `${item.memoryBodyId ?? "unknown"}/${item.id}`));
|
|
11646
11751
|
return {
|
|
11647
11752
|
answer: typeof value.answer === "string" ? value.answer : "",
|
|
@@ -11668,7 +11773,7 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
11668
11773
|
const persona = operation === "supervised-writeback" ? SUPERVISED_WRITE_PERSONA : autonomous ? AUTONOMOUS_WRITE_PERSONA : WRITE_PERSONA;
|
|
11669
11774
|
const terminalTool = WRITE_OPERATION_RESULT_TOOL[operation];
|
|
11670
11775
|
const { provider, runId, result, receipts, writes } = await this.delegate(parent, "write", `Mnemon ${operation}`, prompt, autonomous ? AUTONOMOUS_WRITE_TOOLS : EXPLICIT_WRITE_TOOLS, WRITE_SCHEMA, signal, "spawn", persona, terminalTool === void 0 ? void 0 : { terminalTools: [terminalTool] }, inactive === void 0 ? void 0 : spaceWriteGuard(target, inactive));
|
|
11671
|
-
const value = object$
|
|
11776
|
+
const value = object$7(result.structured);
|
|
11672
11777
|
const observed = terminalTool === void 0 ? void 0 : recoverStructuredResult({ terminalTools: [terminalTool] }, receipts);
|
|
11673
11778
|
const claimed = typeof observed?.action === "string" ? observed.action : typeof value.action === "string" ? value.action : "failed";
|
|
11674
11779
|
if (!autonomous) return {
|
|
@@ -11777,7 +11882,7 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
11777
11882
|
const base = reviewPersona(tools.includes("mnemon_runtime_memory"));
|
|
11778
11883
|
const persona = preferred === "fork" ? base : base.replaceAll("inherited", "supplied bounded");
|
|
11779
11884
|
const { provider, runId, result } = await this.delegate(parent, "review", "Mnemon idle checkpoint review", prompt, tools, WRITE_SCHEMA, execution.signal, preferred, persona, { terminalTools: writers }, void 0, void 0, admission);
|
|
11780
|
-
const value = object$
|
|
11885
|
+
const value = object$7(result.structured);
|
|
11781
11886
|
return {
|
|
11782
11887
|
delegated: true,
|
|
11783
11888
|
runId,
|
|
@@ -11948,7 +12053,7 @@ This is the only completion channel for this run. Do not finish with a plain-tex
|
|
|
11948
12053
|
"failed",
|
|
11949
12054
|
"partial",
|
|
11950
12055
|
"unknown"
|
|
11951
|
-
].includes(String(object$
|
|
12056
|
+
].includes(String(object$7(structured).action))) throw new Error(`memory review reported ${object$7(structured).action}`);
|
|
11952
12057
|
completed = {
|
|
11953
12058
|
provider,
|
|
11954
12059
|
runId: activeRun.id,
|
|
@@ -12111,7 +12216,7 @@ const MNEMON_ACTIVITY_SCHEMA = "dsh-mnemon.activity/v1";
|
|
|
12111
12216
|
const MAX_ITEMS = 8;
|
|
12112
12217
|
const MAX_TITLE = 160;
|
|
12113
12218
|
const MAX_EXCERPT = 500;
|
|
12114
|
-
function object$
|
|
12219
|
+
function object$6(value) {
|
|
12115
12220
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
12116
12221
|
}
|
|
12117
12222
|
function bounded(value, maximum) {
|
|
@@ -12121,7 +12226,7 @@ function bounded(value, maximum) {
|
|
|
12121
12226
|
return truncate(normalized, maximum);
|
|
12122
12227
|
}
|
|
12123
12228
|
function item(value, index) {
|
|
12124
|
-
const entry = object$
|
|
12229
|
+
const entry = object$6(value);
|
|
12125
12230
|
if (entry === void 0) return void 0;
|
|
12126
12231
|
const content = bounded(entry.content ?? entry.text ?? entry.excerpt, MAX_EXCERPT);
|
|
12127
12232
|
const explicitTitle = bounded(entry.title ?? entry.label ?? entry.name ?? entry.memoryBodyName, MAX_TITLE);
|
|
@@ -12135,7 +12240,7 @@ function item(value, index) {
|
|
|
12135
12240
|
};
|
|
12136
12241
|
}
|
|
12137
12242
|
function resultItems(value) {
|
|
12138
|
-
const root = object$
|
|
12243
|
+
const root = object$6(value);
|
|
12139
12244
|
if (root === void 0) return [];
|
|
12140
12245
|
const candidates = [
|
|
12141
12246
|
root.results,
|
|
@@ -12154,8 +12259,8 @@ function resultItems(value) {
|
|
|
12154
12259
|
return items;
|
|
12155
12260
|
}
|
|
12156
12261
|
function writeItem(argsValue, operationId) {
|
|
12157
|
-
const outer = object$
|
|
12158
|
-
const input = object$
|
|
12262
|
+
const outer = object$6(argsValue) ?? {};
|
|
12263
|
+
const input = object$6(outer.input) ?? outer;
|
|
12159
12264
|
const content = bounded(input.content ?? input.description ?? input.reason ?? input.summary ?? input.old_text ?? input.oldText, MAX_EXCERPT);
|
|
12160
12265
|
const explicitTitle = bounded(input.title ?? input.name, MAX_TITLE);
|
|
12161
12266
|
const identity = bounded(input.id ?? input.memoryBodyId ?? input.targetMemoryBodyId ?? input.sourceId, 300);
|
|
@@ -12167,7 +12272,7 @@ function writeItem(argsValue, operationId) {
|
|
|
12167
12272
|
};
|
|
12168
12273
|
}
|
|
12169
12274
|
function reference(argsValue, key) {
|
|
12170
|
-
return bounded(object$
|
|
12275
|
+
return bounded(object$6(argsValue)?.[key], 600);
|
|
12171
12276
|
}
|
|
12172
12277
|
function memoryReadPresentation(sourceTypeId, operationId, referenceKey) {
|
|
12173
12278
|
return (args, value) => ({
|
|
@@ -12186,7 +12291,7 @@ function memoryWritePresentation(sourceTypeId, operationId, referenceKey) {
|
|
|
12186
12291
|
operationId,
|
|
12187
12292
|
...sourceTypeId === void 0 ? {} : { sourceTypeId },
|
|
12188
12293
|
...referenceKey === void 0 || reference(args, referenceKey) === void 0 ? {} : { reference: reference(args, referenceKey) },
|
|
12189
|
-
committed: mutationResultCommitted(value) || operationId === "mutate" && object$
|
|
12294
|
+
committed: mutationResultCommitted(value) || operationId === "mutate" && object$6(value)?.success === true,
|
|
12190
12295
|
item: writeItem(args, operationId)
|
|
12191
12296
|
});
|
|
12192
12297
|
}
|
|
@@ -12196,7 +12301,7 @@ function namedToolPresentation(name) {
|
|
|
12196
12301
|
return tool.kind === "write" ? memoryWritePresentation(tool.sourceTypeId, tool.operation) : memoryReadPresentation(tool.sourceTypeId, tool.operation);
|
|
12197
12302
|
}
|
|
12198
12303
|
function validItem(value) {
|
|
12199
|
-
const entry = object$
|
|
12304
|
+
const entry = object$6(value);
|
|
12200
12305
|
return entry !== void 0 && bounded(entry.id, 300) === entry.id && bounded(entry.title, MAX_TITLE) === entry.title && (entry.excerpt === void 0 || bounded(entry.excerpt, MAX_EXCERPT) === entry.excerpt);
|
|
12201
12306
|
}
|
|
12202
12307
|
function cleanItem(value) {
|
|
@@ -12209,7 +12314,7 @@ function cleanItem(value) {
|
|
|
12209
12314
|
}
|
|
12210
12315
|
/** Fail closed on arbitrary third-party/session metadata. */
|
|
12211
12316
|
function parseMnemonActivityMeta(value, callId, toolName) {
|
|
12212
|
-
const meta = object$
|
|
12317
|
+
const meta = object$6(value);
|
|
12213
12318
|
if (meta?.schema !== "dsh-mnemon.activity/v1" || meta.kind !== "read" && meta.kind !== "write") return {};
|
|
12214
12319
|
const operationId = bounded(meta.operationId, 300);
|
|
12215
12320
|
const sourceTypeId = bounded(meta.sourceTypeId, 128);
|
|
@@ -12778,6 +12883,7 @@ var MnemonAgentLifecycle = class {
|
|
|
12778
12883
|
reviewController;
|
|
12779
12884
|
reviewRunning = false;
|
|
12780
12885
|
reviewAccessBlocked = false;
|
|
12886
|
+
reviewAdmission;
|
|
12781
12887
|
lastReviewAt;
|
|
12782
12888
|
lastReviewAction;
|
|
12783
12889
|
lastReviewScore;
|
|
@@ -12815,6 +12921,10 @@ var MnemonAgentLifecycle = class {
|
|
|
12815
12921
|
this.agent.ctx.on("session/event", ((session, event) => this.sessionEvent(session, event))),
|
|
12816
12922
|
this.agent.ctx.on("system-prompt/assemble", ((assembly, context, next) => this.assemblePrompt(assembly, context, next))),
|
|
12817
12923
|
this.agent.ctx.on("agent/pre-step", ((payload, next) => this.preStep(payload, next)), { prepend: true }),
|
|
12924
|
+
this.agent.ctx.on("agent/request", (async (payload, next) => {
|
|
12925
|
+
await this.prepareReviewAccess(payload);
|
|
12926
|
+
return next();
|
|
12927
|
+
}), { prepend: true }),
|
|
12818
12928
|
this.agent.ctx.on("agent/turn-stopping", ((payload) => this.finishTurn(payload)))
|
|
12819
12929
|
];
|
|
12820
12930
|
return () => {
|
|
@@ -12959,18 +13069,28 @@ var MnemonAgentLifecycle = class {
|
|
|
12959
13069
|
activity.toolCallCount = tools.count;
|
|
12960
13070
|
activity.toolNames = tools.names;
|
|
12961
13071
|
if (!this.reviewActivity().eligible || !this.reviewAdmitted(payload.turn)) return;
|
|
13072
|
+
const admission = this.reviewAdmission;
|
|
13073
|
+
if (admission?.turn === payload.turn && admission.policy === this.reviewPolicy() && admission.available) this.scheduleIdleReview(payload.turn);
|
|
13074
|
+
}
|
|
13075
|
+
async prepareReviewAccess(payload) {
|
|
13076
|
+
this.recordComposedTurn(payload.turn);
|
|
13077
|
+
if (payload.signal.aborted || !this.restoreWatermark() || !this.idleReviewAllowed() || this.turnActivity.get(payload.turn)?.noMaintenance) return;
|
|
13078
|
+
const policy = this.reviewPolicy();
|
|
13079
|
+
if (this.reviewAdmission?.turn === payload.turn && this.reviewAdmission.policy === policy) return;
|
|
12962
13080
|
const controller = new AbortController();
|
|
12963
13081
|
const signal = AbortSignal.any([payload.signal, controller.signal]);
|
|
12964
13082
|
const epoch = this.reviewEpoch;
|
|
12965
|
-
const policy = this.reviewPolicy();
|
|
12966
13083
|
this.reviewController = controller;
|
|
12967
13084
|
this.reviewRunning = true;
|
|
12968
|
-
let admitted = false;
|
|
12969
13085
|
try {
|
|
12970
13086
|
const available = await this.coordinator.checkReviewAccess(this.agent, signal);
|
|
12971
13087
|
if (signal.aborted || epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed()) return;
|
|
12972
13088
|
this.reviewAccessBlocked = !available;
|
|
12973
|
-
|
|
13089
|
+
this.reviewAdmission = {
|
|
13090
|
+
turn: payload.turn,
|
|
13091
|
+
policy,
|
|
13092
|
+
available
|
|
13093
|
+
};
|
|
12974
13094
|
} catch (error) {
|
|
12975
13095
|
if (!signal.aborted) this.fail(error);
|
|
12976
13096
|
} finally {
|
|
@@ -12979,7 +13099,6 @@ var MnemonAgentLifecycle = class {
|
|
|
12979
13099
|
this.reviewController = void 0;
|
|
12980
13100
|
}
|
|
12981
13101
|
}
|
|
12982
|
-
if (admitted) this.scheduleIdleReview(payload.turn);
|
|
12983
13102
|
}
|
|
12984
13103
|
sessionEvent(session, event) {
|
|
12985
13104
|
if (session !== this.agent.session) return;
|
|
@@ -13072,6 +13191,7 @@ var MnemonAgentLifecycle = class {
|
|
|
13072
13191
|
if (policy !== this.observedReviewPolicy) {
|
|
13073
13192
|
this.cancelIdleReview(true);
|
|
13074
13193
|
this.reviewAccessBlocked = false;
|
|
13194
|
+
this.reviewAdmission = void 0;
|
|
13075
13195
|
}
|
|
13076
13196
|
this.observedReviewPolicy = policy;
|
|
13077
13197
|
this.restoreWatermark();
|
|
@@ -13871,6 +13991,16 @@ var MnemonLifecycle = class {
|
|
|
13871
13991
|
...typeof effort === "string" && effort !== "" ? { reasoningEffort: effort } : {}
|
|
13872
13992
|
};
|
|
13873
13993
|
}
|
|
13994
|
+
/** Freeze the effective task route for a paired skill evaluation. */
|
|
13995
|
+
skillTaskRoute(workspaceRoot) {
|
|
13996
|
+
const options = this.taskAgentModelOptions("", workspaceRoot);
|
|
13997
|
+
if (!options?.provider || !options.model) return void 0;
|
|
13998
|
+
return {
|
|
13999
|
+
provider: options.provider,
|
|
14000
|
+
model: options.model,
|
|
14001
|
+
...options.reasoningEffort === void 0 ? {} : { reasoningEffort: options.reasoningEffort }
|
|
14002
|
+
};
|
|
14003
|
+
}
|
|
13874
14004
|
taskAgentModelOptions(fallbackSessionId, workspaceRoot) {
|
|
13875
14005
|
return this.taskAgentModelRoute(fallbackSessionId, workspaceRoot)?.options;
|
|
13876
14006
|
}
|
|
@@ -13958,12 +14088,11 @@ var MnemonLifecycle = class {
|
|
|
13958
14088
|
return decision;
|
|
13959
14089
|
}
|
|
13960
14090
|
if (openAgentTurn(agent) !== payload.turn) return decision;
|
|
13961
|
-
const recovering = memory.current?.turn !== payload.turn;
|
|
13962
14091
|
await memory.begin(payload.turn, payload.signal);
|
|
13963
14092
|
const pinned = memory.current;
|
|
13964
|
-
if (
|
|
13965
|
-
const wake =
|
|
13966
|
-
if (wake.text.trim() !== "") return {
|
|
14093
|
+
if (payload.step === 1 && this.config.lifecycleEnabled && pinned !== void 0) {
|
|
14094
|
+
const wake = memory.modelWake();
|
|
14095
|
+
if (wake !== void 0 && wake.text.trim() !== "") return {
|
|
13967
14096
|
kind: "enter",
|
|
13968
14097
|
messages: [...decision.messages, createPluginMessage(wake.text, "recall")]
|
|
13969
14098
|
};
|
|
@@ -13983,6 +14112,228 @@ var MnemonLifecycle = class {
|
|
|
13983
14112
|
}
|
|
13984
14113
|
};
|
|
13985
14114
|
//#endregion
|
|
14115
|
+
//#region src/host/skill-auto-adoption.ts
|
|
14116
|
+
var AutomaticSkillStop = class extends Error {
|
|
14117
|
+
reason;
|
|
14118
|
+
constructor(reason, detail) {
|
|
14119
|
+
super(detail ?? reason);
|
|
14120
|
+
this.reason = reason;
|
|
14121
|
+
}
|
|
14122
|
+
};
|
|
14123
|
+
function hashSkillCases(cases) {
|
|
14124
|
+
return createHash("sha256").update(SkillsContracts.skillCaseSignature(cases)).digest("hex");
|
|
14125
|
+
}
|
|
14126
|
+
function hashSkillDraft(draft) {
|
|
14127
|
+
return createHash("sha256").update(draft.content).digest("hex");
|
|
14128
|
+
}
|
|
14129
|
+
/** Only fresh idle-review suggestions enter this bounded, in-process queue. */
|
|
14130
|
+
var SkillAutoAdoption = class {
|
|
14131
|
+
host;
|
|
14132
|
+
jobs = /* @__PURE__ */ new Map();
|
|
14133
|
+
tail = Promise.resolve();
|
|
14134
|
+
disposed = false;
|
|
14135
|
+
constructor(host) {
|
|
14136
|
+
this.host = host;
|
|
14137
|
+
}
|
|
14138
|
+
enqueue(source, workspace, ids, available) {
|
|
14139
|
+
if (this.disposed || !this.host.settings().autoAdopt) return;
|
|
14140
|
+
for (const id of ids) {
|
|
14141
|
+
if (this.jobs.has(id)) continue;
|
|
14142
|
+
if (this.jobs.size >= 16) {
|
|
14143
|
+
this.record({
|
|
14144
|
+
source,
|
|
14145
|
+
id
|
|
14146
|
+
}, {
|
|
14147
|
+
stage: "review",
|
|
14148
|
+
attempt: 0,
|
|
14149
|
+
reason: "queue-full"
|
|
14150
|
+
}).catch((error) => this.host.warn("Could not record automatic adoption queue limit", error));
|
|
14151
|
+
continue;
|
|
14152
|
+
}
|
|
14153
|
+
const job = {
|
|
14154
|
+
source,
|
|
14155
|
+
workspace,
|
|
14156
|
+
id,
|
|
14157
|
+
available,
|
|
14158
|
+
controller: new AbortController()
|
|
14159
|
+
};
|
|
14160
|
+
this.jobs.set(id, job);
|
|
14161
|
+
this.tail = this.tail.then(() => this.run(job)).catch((error) => this.host.warn("Automatic skill adoption failed", error)).finally(() => {
|
|
14162
|
+
this.jobs.delete(id);
|
|
14163
|
+
});
|
|
14164
|
+
}
|
|
14165
|
+
}
|
|
14166
|
+
allowed(job) {
|
|
14167
|
+
const settings = this.host.settings();
|
|
14168
|
+
return !this.disposed && settings.evolution && settings.autoAdopt && settings.autoBacktest && job.available() && (job.action === void 0 || (job.action === "create" ? settings.allowCreate : settings.allowUpdate));
|
|
14169
|
+
}
|
|
14170
|
+
settingsChanged() {
|
|
14171
|
+
for (const job of this.jobs.values()) if (!this.allowed(job)) job.controller.abort(new AutomaticSkillStop("disabled"));
|
|
14172
|
+
}
|
|
14173
|
+
dispose() {
|
|
14174
|
+
this.disposed = true;
|
|
14175
|
+
for (const job of this.jobs.values()) job.controller.abort(new AutomaticSkillStop("interrupted"));
|
|
14176
|
+
}
|
|
14177
|
+
checkpoint(job) {
|
|
14178
|
+
if (!this.allowed(job)) job.controller.abort(new AutomaticSkillStop(this.disposed ? "interrupted" : "disabled"));
|
|
14179
|
+
job.controller.signal.throwIfAborted();
|
|
14180
|
+
}
|
|
14181
|
+
record(job, event) {
|
|
14182
|
+
return job.source.mutate("auto-adoption-record", {
|
|
14183
|
+
id: job.id,
|
|
14184
|
+
...event
|
|
14185
|
+
});
|
|
14186
|
+
}
|
|
14187
|
+
async unchanged(job, draft, casesHash) {
|
|
14188
|
+
this.checkpoint(job);
|
|
14189
|
+
const current = await job.source.read("draft", { id: draft.id }, job.controller.signal);
|
|
14190
|
+
const suggestion = (await job.source.read("suggestions", null, job.controller.signal)).find((entry) => entry.id === job.id);
|
|
14191
|
+
const cases = (await job.source.read("cases", { skill: draft.skillName }, job.controller.signal)).filter((entry) => entry.enabled);
|
|
14192
|
+
if (current.conflict !== void 0 || current.draft.status !== "ready" || suggestion?.status !== "review" || suggestion.draftId !== draft.id || hashSkillDraft(current.draft) !== hashSkillDraft(draft) || hashSkillCases(cases) !== casesHash) throw new AutomaticSkillStop("changed");
|
|
14193
|
+
}
|
|
14194
|
+
async runs(job, ids) {
|
|
14195
|
+
const deadline = AbortSignal.timeout(276e4);
|
|
14196
|
+
const signal = AbortSignal.any([job.controller.signal, deadline]);
|
|
14197
|
+
for (;;) {
|
|
14198
|
+
this.checkpoint(job);
|
|
14199
|
+
signal.throwIfAborted();
|
|
14200
|
+
const runs = await Promise.all(ids.map((id) => job.source.read("run", { id }, signal)));
|
|
14201
|
+
if (runs.some((run) => run.status !== "running" && run.status !== "completed") || runs.every((run) => run.status !== "running")) return runs;
|
|
14202
|
+
await setTimeout$1(250, void 0, { signal });
|
|
14203
|
+
}
|
|
14204
|
+
}
|
|
14205
|
+
async run(job) {
|
|
14206
|
+
const runIds = /* @__PURE__ */ new Set();
|
|
14207
|
+
let attempt = 0;
|
|
14208
|
+
let scores = {};
|
|
14209
|
+
try {
|
|
14210
|
+
this.checkpoint(job);
|
|
14211
|
+
const suggestion = (await job.source.read("suggestions")).find((entry) => entry.id === job.id);
|
|
14212
|
+
if (suggestion?.status !== "pending" || suggestion.draftId !== void 0) throw new AutomaticSkillStop("changed");
|
|
14213
|
+
job.action = suggestion.action;
|
|
14214
|
+
this.checkpoint(job);
|
|
14215
|
+
const settings = this.host.settings();
|
|
14216
|
+
const reserved = await job.source.mutate("auto-adoption-reserve", {
|
|
14217
|
+
id: job.id,
|
|
14218
|
+
limit: settings.autoAdoptDailyLimit,
|
|
14219
|
+
timeZone: settings.autoAdoptTimeZone
|
|
14220
|
+
}, job.controller.signal);
|
|
14221
|
+
if (!reserved.allowed) throw new AutomaticSkillStop(reserved.reason, `${reserved.used}/${reserved.limit} · ${reserved.date} · ${reserved.timeZone}`);
|
|
14222
|
+
await this.record(job, {
|
|
14223
|
+
stage: "queued",
|
|
14224
|
+
attempt,
|
|
14225
|
+
detail: `${reserved.used}/${reserved.limit} · ${reserved.date} · ${reserved.timeZone}`
|
|
14226
|
+
});
|
|
14227
|
+
const cases = (await job.source.read("cases", { skill: suggestion.skillName })).filter((entry) => entry.enabled);
|
|
14228
|
+
if (cases.length === 0) throw new AutomaticSkillStop("no-cases");
|
|
14229
|
+
if (cases.some((entry) => entry.requiresVision === true || entry.checks.length === 0)) throw new AutomaticSkillStop("unmeasured");
|
|
14230
|
+
if (cases.length > this.host.settings().casesPerRun) throw new AutomaticSkillStop("case-limit");
|
|
14231
|
+
const casesHash = hashSkillCases(cases);
|
|
14232
|
+
const preferences = await job.source.read("draft-preferences");
|
|
14233
|
+
const defaultRoute = this.host.route(job.workspace);
|
|
14234
|
+
const runRoute = preferences.skillRunModels[suggestion.skillName] ?? preferences.runModel ?? defaultRoute;
|
|
14235
|
+
const draftRoute = preferences.suggestionModels[job.id] ?? preferences.model ?? defaultRoute;
|
|
14236
|
+
if (runRoute === void 0 || draftRoute === void 0) throw new AutomaticSkillStop("model-unavailable");
|
|
14237
|
+
let previous;
|
|
14238
|
+
let guidance = "";
|
|
14239
|
+
for (attempt = 1; attempt <= 3; attempt++) {
|
|
14240
|
+
this.checkpoint(job);
|
|
14241
|
+
await this.record(job, {
|
|
14242
|
+
stage: "drafting",
|
|
14243
|
+
attempt
|
|
14244
|
+
});
|
|
14245
|
+
const draft = await this.host.draft(job.source, job.workspace, {
|
|
14246
|
+
suggestionId: job.id,
|
|
14247
|
+
action: suggestion.action,
|
|
14248
|
+
...previous === void 0 ? {} : { previous },
|
|
14249
|
+
guidance,
|
|
14250
|
+
route: draftRoute
|
|
14251
|
+
}, job.controller.signal);
|
|
14252
|
+
await this.unchanged(job, draft, casesHash);
|
|
14253
|
+
await this.record(job, {
|
|
14254
|
+
stage: "backtesting",
|
|
14255
|
+
attempt
|
|
14256
|
+
});
|
|
14257
|
+
const baseline = await this.host.backtest(job.source, job.workspace, {
|
|
14258
|
+
skillName: draft.skillName,
|
|
14259
|
+
target: {
|
|
14260
|
+
kind: draft.action === "update" ? "current" : "none",
|
|
14261
|
+
...draft.action === "update" ? { draftId: draft.id } : {}
|
|
14262
|
+
},
|
|
14263
|
+
route: runRoute,
|
|
14264
|
+
expectedCasesHash: casesHash,
|
|
14265
|
+
signal: job.controller.signal
|
|
14266
|
+
});
|
|
14267
|
+
runIds.add(baseline.runId);
|
|
14268
|
+
this.checkpoint(job);
|
|
14269
|
+
const candidate = await this.host.backtest(job.source, job.workspace, {
|
|
14270
|
+
skillName: draft.skillName,
|
|
14271
|
+
target: {
|
|
14272
|
+
kind: "draft",
|
|
14273
|
+
draftId: draft.id
|
|
14274
|
+
},
|
|
14275
|
+
route: runRoute,
|
|
14276
|
+
expectedCasesHash: casesHash,
|
|
14277
|
+
signal: job.controller.signal
|
|
14278
|
+
});
|
|
14279
|
+
runIds.add(candidate.runId);
|
|
14280
|
+
await job.source.mutate("run-link", {
|
|
14281
|
+
draftId: draft.id,
|
|
14282
|
+
runId: baseline.runId
|
|
14283
|
+
}, job.controller.signal);
|
|
14284
|
+
const [before, after] = await this.runs(job, [baseline.runId, candidate.runId]);
|
|
14285
|
+
await this.unchanged(job, draft, casesHash);
|
|
14286
|
+
const comparison = SkillsContracts.compareAutomaticSkillRuns(cases, before, after, casesHash);
|
|
14287
|
+
scores = {
|
|
14288
|
+
...comparison.baselineScore === void 0 ? {} : { baselineScore: comparison.baselineScore },
|
|
14289
|
+
...comparison.draftScore === void 0 ? {} : { draftScore: comparison.draftScore }
|
|
14290
|
+
};
|
|
14291
|
+
if (comparison.outcome === "unmeasured") throw new AutomaticSkillStop("unmeasured");
|
|
14292
|
+
if (comparison.outcome === "not-worse") {
|
|
14293
|
+
this.checkpoint(job);
|
|
14294
|
+
await job.source.mutate("draft-adopt", {
|
|
14295
|
+
id: draft.id,
|
|
14296
|
+
automatic: {
|
|
14297
|
+
reservationId: reserved.reservationId,
|
|
14298
|
+
draftHash: hashSkillDraft(draft),
|
|
14299
|
+
baselineRunId: baseline.runId,
|
|
14300
|
+
draftRunId: candidate.runId,
|
|
14301
|
+
casesHash,
|
|
14302
|
+
attempt
|
|
14303
|
+
}
|
|
14304
|
+
}, job.controller.signal);
|
|
14305
|
+
return;
|
|
14306
|
+
}
|
|
14307
|
+
if (attempt === 3) throw new AutomaticSkillStop("regressed");
|
|
14308
|
+
await this.record(job, {
|
|
14309
|
+
stage: "rewriting",
|
|
14310
|
+
attempt,
|
|
14311
|
+
reason: "regressed",
|
|
14312
|
+
...scores
|
|
14313
|
+
});
|
|
14314
|
+
guidance = `The previous draft regressed in paired backtests. Baseline score: ${comparison.baselineScore}; previous draft score: ${comparison.draftScore}. Improve each regressed case without losing any previously passing check.\n` + JSON.stringify(cases.map((entry) => ({
|
|
14315
|
+
title: entry.title,
|
|
14316
|
+
baseline: before.results.find((result) => result.caseId === entry.id),
|
|
14317
|
+
draft: after.results.find((result) => result.caseId === entry.id)
|
|
14318
|
+
}))).slice(0, 8e3);
|
|
14319
|
+
previous = draft;
|
|
14320
|
+
}
|
|
14321
|
+
} catch (error) {
|
|
14322
|
+
const failure = job.controller.signal.aborted ? job.controller.signal.reason : error;
|
|
14323
|
+
const reason = failure instanceof AutomaticSkillStop ? failure.reason : "failed";
|
|
14324
|
+
await this.record(job, {
|
|
14325
|
+
stage: reason === "disabled" || reason === "interrupted" ? "paused" : "review",
|
|
14326
|
+
attempt: Math.min(attempt, 3),
|
|
14327
|
+
reason,
|
|
14328
|
+
...scores,
|
|
14329
|
+
...failure instanceof AutomaticSkillStop && failure.message === failure.reason ? {} : { detail: failure instanceof Error ? failure.message : String(failure) }
|
|
14330
|
+
}).catch((recordError) => this.host.warn("Could not record automatic adoption outcome", recordError));
|
|
14331
|
+
} finally {
|
|
14332
|
+
for (const id of runIds) await job.source.mutate("run-cancel", { id }).catch((error) => this.host.warn("Could not stop automatic skill backtest", error));
|
|
14333
|
+
}
|
|
14334
|
+
}
|
|
14335
|
+
};
|
|
14336
|
+
//#endregion
|
|
13986
14337
|
//#region src/host/skill-review.ts
|
|
13987
14338
|
const SIGNALS = [
|
|
13988
14339
|
"correction",
|
|
@@ -14815,7 +15166,7 @@ function retryingSource(source, attempts = 4) {
|
|
|
14815
15166
|
for (let attempt = 1;; attempt++) try {
|
|
14816
15167
|
return await source.mutate(operation, input, signal);
|
|
14817
15168
|
} catch (error) {
|
|
14818
|
-
if (attempt >= attempts || !/revision conflict|changed since it was read; reload/u.test(error instanceof Error ? error.message : String(error))) throw error;
|
|
15169
|
+
if (attempt >= attempts || !/revision conflict|Skills Source changed since it was read; reload/u.test(error instanceof Error ? error.message : String(error))) throw error;
|
|
14819
15170
|
}
|
|
14820
15171
|
}
|
|
14821
15172
|
};
|
|
@@ -14823,6 +15174,27 @@ function retryingSource(source, attempts = 4) {
|
|
|
14823
15174
|
function message(error) {
|
|
14824
15175
|
return redact(error instanceof Error ? error.message : String(error));
|
|
14825
15176
|
}
|
|
15177
|
+
/**
|
|
15178
|
+
* A draft task that stops reporting is abandoned here. The subagent behind it
|
|
15179
|
+
* can die without settling, and the suggestion would otherwise stay `drafting`
|
|
15180
|
+
* - blocking dismiss, delete and a retry - until the Host restarts.
|
|
15181
|
+
*/
|
|
15182
|
+
const DRAFT_DEADLINE_MS = 9e5;
|
|
15183
|
+
/** Distinguishes a deadline abort from the plain abort a Host shutdown sends. */
|
|
15184
|
+
var DraftDeadlineError = class extends Error {};
|
|
15185
|
+
/** The deadline error a task ran into, so the Source records why the draft ended. */
|
|
15186
|
+
function deadlineReason(signal) {
|
|
15187
|
+
return signal.reason instanceof DraftDeadlineError ? signal.reason : void 0;
|
|
15188
|
+
}
|
|
15189
|
+
/** Cancellation settles the owner even when an underlying task ignores its signal. */
|
|
15190
|
+
function abortable(signal, work) {
|
|
15191
|
+
signal.throwIfAborted();
|
|
15192
|
+
return new Promise((resolve, reject) => {
|
|
15193
|
+
const abort = () => reject(signal.reason);
|
|
15194
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
15195
|
+
Promise.resolve().then(work).then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
15196
|
+
});
|
|
15197
|
+
}
|
|
14826
15198
|
/** Resolve only roots supplied by this Source's scoped catalog, never the task's cwd. */
|
|
14827
15199
|
function newSkillRoot(settings, source, catalog, explicit) {
|
|
14828
15200
|
let root = explicit;
|
|
@@ -14830,7 +15202,9 @@ function newSkillRoot(settings, source, catalog, explicit) {
|
|
|
14830
15202
|
if (settings.targetScope === "storage" && source.scope === void 0) throw new Error("The skill destination needs a known storage scope; select User or Project in Skills settings.");
|
|
14831
15203
|
root = settings.targetScope === "project" || settings.targetScope === "storage" && (source.scope?.storage === "workspace" || source.scope?.storage === "workspaces") ? "project-dsh" : "user-dsh";
|
|
14832
15204
|
}
|
|
14833
|
-
const
|
|
15205
|
+
const destination = catalog.roots.find((candidate) => candidate.kind === root);
|
|
15206
|
+
if (destination?.readOnly) throw new Error(`Cannot create a skill in ${root}: linked skill roots are read-only.`);
|
|
15207
|
+
const directory = destination?.path;
|
|
14834
15208
|
if (directory === void 0 || root.startsWith("project-") && catalog.projectRoot === void 0) throw new Error("Project skill roots need a selected workspace; select a workspace or choose User in Skills settings.");
|
|
14835
15209
|
if (root.startsWith("project-")) try {
|
|
14836
15210
|
if (!statSync(catalog.projectRoot).isDirectory()) throw new Error("not a directory");
|
|
@@ -14863,8 +15237,22 @@ var SkillEvolution = class {
|
|
|
14863
15237
|
sessions = /* @__PURE__ */ new Map();
|
|
14864
15238
|
jobs = /* @__PURE__ */ new Set();
|
|
14865
15239
|
disposed = false;
|
|
15240
|
+
activeBacktests = 0;
|
|
15241
|
+
backtestJobs = 0;
|
|
15242
|
+
backtestWaiters = /* @__PURE__ */ new Set();
|
|
15243
|
+
automatic;
|
|
14866
15244
|
constructor(host) {
|
|
14867
15245
|
this.host = host;
|
|
15246
|
+
this.automatic = new SkillAutoAdoption({
|
|
15247
|
+
settings: () => host.settings(),
|
|
15248
|
+
route: (workspace) => host.taskRoute?.(workspace),
|
|
15249
|
+
draft: (source, workspace, input, signal) => this.automaticDraft(source, workspace, input, signal),
|
|
15250
|
+
backtest: (source, workspace, input) => this.backtest(source, workspace, input),
|
|
15251
|
+
warn: (text, error) => this.warn(text, error)
|
|
15252
|
+
});
|
|
15253
|
+
}
|
|
15254
|
+
settingsChanged() {
|
|
15255
|
+
this.automatic.settingsChanged();
|
|
14868
15256
|
}
|
|
14869
15257
|
source(agent) {
|
|
14870
15258
|
const source = this.host.source(agent);
|
|
@@ -15098,6 +15486,7 @@ var SkillEvolution = class {
|
|
|
15098
15486
|
});
|
|
15099
15487
|
state.failures = 0;
|
|
15100
15488
|
state.lastReviewAt = this.now().getTime();
|
|
15489
|
+
if (trigger === "automatic" && result.createdSuggestionIds !== void 0) this.automatic.enqueue(source, workspace, result.createdSuggestionIds, () => this.host.source(agent) !== void 0);
|
|
15101
15490
|
return result;
|
|
15102
15491
|
} catch (error) {
|
|
15103
15492
|
state.failures += 1;
|
|
@@ -15128,10 +15517,17 @@ var SkillEvolution = class {
|
|
|
15128
15517
|
delete state.reviewing;
|
|
15129
15518
|
}
|
|
15130
15519
|
}
|
|
15131
|
-
background(name, work) {
|
|
15520
|
+
background(name, work, deadlineMs) {
|
|
15132
15521
|
const controller = new AbortController();
|
|
15133
15522
|
this.jobs.add(controller);
|
|
15134
|
-
|
|
15523
|
+
const timer = deadlineMs === void 0 ? void 0 : setTimeout(() => {
|
|
15524
|
+
controller.abort(new DraftDeadlineError(`${name} did not finish within ${Math.round(deadlineMs / 6e4)} minutes`));
|
|
15525
|
+
}, deadlineMs);
|
|
15526
|
+
timer?.unref?.();
|
|
15527
|
+
return work(controller.signal).catch((error) => this.warn(`${name} failed`, deadlineReason(controller.signal) ?? error)).finally(() => {
|
|
15528
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
15529
|
+
this.jobs.delete(controller);
|
|
15530
|
+
});
|
|
15135
15531
|
}
|
|
15136
15532
|
/**
|
|
15137
15533
|
* Write a draft for a suggestion (or for a skill directly) in the
|
|
@@ -15144,6 +15540,7 @@ var SkillEvolution = class {
|
|
|
15144
15540
|
...input.draftId === void 0 ? {} : { draftId: input.draftId },
|
|
15145
15541
|
...input.skill === void 0 ? {} : { skill: input.skill }
|
|
15146
15542
|
});
|
|
15543
|
+
if (pack.skill?.readOnly) throw new Error("Linked skills are read-only; use a local copy before generating an improvement draft.");
|
|
15147
15544
|
const previousId = input.draftId ?? pack.suggestion?.draftId;
|
|
15148
15545
|
const previous = previousId === void 0 ? (await source.read("drafts", {
|
|
15149
15546
|
skill: pack.skillName,
|
|
@@ -15157,26 +15554,84 @@ var SkillEvolution = class {
|
|
|
15157
15554
|
status: "drafting"
|
|
15158
15555
|
});
|
|
15159
15556
|
this.background("skill draft", async (signal) => {
|
|
15557
|
+
let recorded = false;
|
|
15160
15558
|
try {
|
|
15161
|
-
await this.writeDraft(source, workspace, input, {
|
|
15559
|
+
await abortable(signal, () => this.writeDraft(source, workspace, input, {
|
|
15162
15560
|
pack,
|
|
15163
15561
|
targetRoot,
|
|
15164
|
-
guidance
|
|
15165
|
-
|
|
15562
|
+
guidance,
|
|
15563
|
+
onRecorded: () => {
|
|
15564
|
+
recorded = true;
|
|
15565
|
+
}
|
|
15566
|
+
}, signal));
|
|
15166
15567
|
} catch (error) {
|
|
15167
|
-
if (input.suggestionId !== void 0)
|
|
15168
|
-
id
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15568
|
+
if (!recorded && input.suggestionId !== void 0) {
|
|
15569
|
+
if ((await source.read("suggestions")).find((item) => item.id === input.suggestionId)?.status === "drafting") await source.mutate("suggestion-status", {
|
|
15570
|
+
id: input.suggestionId,
|
|
15571
|
+
status: "pending",
|
|
15572
|
+
error: message(deadlineReason(signal) ?? error)
|
|
15573
|
+
}).catch(() => void 0);
|
|
15574
|
+
}
|
|
15172
15575
|
throw error;
|
|
15173
15576
|
}
|
|
15174
|
-
});
|
|
15577
|
+
}, DRAFT_DEADLINE_MS);
|
|
15175
15578
|
return {
|
|
15176
15579
|
started: true,
|
|
15177
15580
|
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId }
|
|
15178
15581
|
};
|
|
15179
15582
|
}
|
|
15583
|
+
async automaticDraft(source, workspace, input, parentSignal) {
|
|
15584
|
+
const controller = new AbortController();
|
|
15585
|
+
const signal = AbortSignal.any([parentSignal, controller.signal]);
|
|
15586
|
+
const timer = setTimeout(() => controller.abort(new DraftDeadlineError("Automatic skill draft did not finish within 15 minutes")), DRAFT_DEADLINE_MS);
|
|
15587
|
+
timer.unref?.();
|
|
15588
|
+
const expectedDraft = input.previous === void 0 ? null : {
|
|
15589
|
+
id: input.previous.id,
|
|
15590
|
+
hash: hashSkillDraft(input.previous)
|
|
15591
|
+
};
|
|
15592
|
+
let recorded = false;
|
|
15593
|
+
let claimed = false;
|
|
15594
|
+
try {
|
|
15595
|
+
return await abortable(signal, async () => {
|
|
15596
|
+
const pack = await source.read("evidence-pack", { suggestionId: input.suggestionId }, signal);
|
|
15597
|
+
signal.throwIfAborted();
|
|
15598
|
+
if (pack.skill?.readOnly) throw new AutomaticSkillStop("changed");
|
|
15599
|
+
if (pack.action !== input.action || pack.action === "update" && pack.suggestion?.skillFileHash !== pack.skill?.hash) throw new AutomaticSkillStop("changed");
|
|
15600
|
+
const targetRoot = pack.action === "update" ? pack.skill.root : newSkillRoot(this.host.settings(), source, await source.read("catalog", null, signal), input.previous?.targetRoot ?? pack.suggestion?.targetRoot);
|
|
15601
|
+
const guidance = [...input.previous?.guidance ?? [], ...input.guidance === "" ? [] : [input.guidance]].slice(-8);
|
|
15602
|
+
await source.mutate("suggestion-status", {
|
|
15603
|
+
id: input.suggestionId,
|
|
15604
|
+
status: "drafting",
|
|
15605
|
+
expectedDraft
|
|
15606
|
+
}, signal);
|
|
15607
|
+
claimed = true;
|
|
15608
|
+
return this.writeDraft(source, workspace, {
|
|
15609
|
+
suggestionId: input.suggestionId,
|
|
15610
|
+
...input.previous === void 0 ? {} : { draftId: input.previous.id },
|
|
15611
|
+
route: input.route
|
|
15612
|
+
}, {
|
|
15613
|
+
pack,
|
|
15614
|
+
targetRoot,
|
|
15615
|
+
guidance,
|
|
15616
|
+
expectedDraft,
|
|
15617
|
+
deferBacktests: true,
|
|
15618
|
+
onRecorded: () => {
|
|
15619
|
+
recorded = true;
|
|
15620
|
+
}
|
|
15621
|
+
}, signal);
|
|
15622
|
+
});
|
|
15623
|
+
} catch (error) {
|
|
15624
|
+
if (claimed && !recorded) await source.mutate("suggestion-status", {
|
|
15625
|
+
id: input.suggestionId,
|
|
15626
|
+
status: "pending",
|
|
15627
|
+
expectedDraft,
|
|
15628
|
+
...parentSignal.aborted ? {} : { error: message(signal.aborted ? signal.reason : error) }
|
|
15629
|
+
}).catch(() => void 0);
|
|
15630
|
+
throw signal.aborted ? signal.reason : error;
|
|
15631
|
+
} finally {
|
|
15632
|
+
clearTimeout(timer);
|
|
15633
|
+
}
|
|
15634
|
+
}
|
|
15180
15635
|
async writeDraft(source, workspace, input, prepared, signal) {
|
|
15181
15636
|
const settings = this.host.settings();
|
|
15182
15637
|
const { pack, targetRoot, guidance } = prepared;
|
|
@@ -15213,15 +15668,19 @@ var SkillEvolution = class {
|
|
|
15213
15668
|
notes: parsed.notes,
|
|
15214
15669
|
changes: parsed.changes,
|
|
15215
15670
|
guidance,
|
|
15671
|
+
expectedBaseHash: pack.skill?.hash ?? null,
|
|
15672
|
+
...prepared.expectedDraft === void 0 ? {} : { expectedDraft: prepared.expectedDraft },
|
|
15216
15673
|
...pack.action === "create" ? { targetRoot } : {}
|
|
15217
15674
|
}, signal);
|
|
15675
|
+
prepared.onRecorded?.();
|
|
15218
15676
|
} catch (error) {
|
|
15219
15677
|
signal.throwIfAborted();
|
|
15220
|
-
if (attempt > 0) throw error;
|
|
15678
|
+
if (attempt > 0 || /changed while this draft/.test(message(error))) throw error;
|
|
15221
15679
|
prompt += `\n\nYour previous SKILL.md was rejected: ${message(error)}. Return a corrected complete file.`;
|
|
15222
15680
|
}
|
|
15223
15681
|
}
|
|
15224
|
-
|
|
15682
|
+
signal.throwIfAborted();
|
|
15683
|
+
if (!prepared.deferBacktests && (settings.autoBacktest || input.backtest) && pack.cases.length > 0) {
|
|
15225
15684
|
await this.backtest(source, workspace, {
|
|
15226
15685
|
skillName: pack.skillName,
|
|
15227
15686
|
target: {
|
|
@@ -15252,67 +15711,138 @@ var SkillEvolution = class {
|
|
|
15252
15711
|
return draft;
|
|
15253
15712
|
}
|
|
15254
15713
|
/** Start a backtest of one version of a skill against its enabled cases; it runs in the background. */
|
|
15714
|
+
backtestSlot(signal) {
|
|
15715
|
+
signal.throwIfAborted();
|
|
15716
|
+
return new Promise((resolve, reject) => {
|
|
15717
|
+
const abort = () => {
|
|
15718
|
+
this.backtestWaiters.delete(acquire);
|
|
15719
|
+
reject(signal.reason);
|
|
15720
|
+
};
|
|
15721
|
+
const acquire = () => {
|
|
15722
|
+
if (signal.aborted) {
|
|
15723
|
+
abort();
|
|
15724
|
+
return;
|
|
15725
|
+
}
|
|
15726
|
+
if (this.activeBacktests >= 2) return;
|
|
15727
|
+
this.backtestWaiters.delete(acquire);
|
|
15728
|
+
signal.removeEventListener("abort", abort);
|
|
15729
|
+
this.activeBacktests++;
|
|
15730
|
+
resolve(() => {
|
|
15731
|
+
this.activeBacktests--;
|
|
15732
|
+
for (const waiter of [...this.backtestWaiters]) waiter();
|
|
15733
|
+
});
|
|
15734
|
+
};
|
|
15735
|
+
this.backtestWaiters.add(acquire);
|
|
15736
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
15737
|
+
acquire();
|
|
15738
|
+
});
|
|
15739
|
+
}
|
|
15255
15740
|
async backtest(port, workspace, input) {
|
|
15741
|
+
if (this.disposed) throw new Error("Skill evolution has stopped");
|
|
15742
|
+
input.signal?.throwIfAborted();
|
|
15743
|
+
if (this.backtestJobs >= 16) throw new Error("The skill backtest queue is full. Cancel a run or retry after it finishes.");
|
|
15744
|
+
this.backtestJobs++;
|
|
15256
15745
|
const source = retryingSource(port);
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
|
|
15276
|
-
|
|
15277
|
-
|
|
15278
|
-
|
|
15279
|
-
|
|
15280
|
-
|
|
15281
|
-
|
|
15282
|
-
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15746
|
+
try {
|
|
15747
|
+
const settings = this.host.settings();
|
|
15748
|
+
const cases = (await source.read("cases", { skill: input.skillName }, input.signal)).filter((entry) => entry.enabled && (input.caseIds === void 0 || input.caseIds.includes(entry.id))).slice(0, settings.casesPerRun);
|
|
15749
|
+
if (cases.length === 0) throw new Error(`${input.skillName} has no enabled eval case to run`);
|
|
15750
|
+
if (input.expectedCasesHash !== void 0 && hashSkillCases(cases) !== input.expectedCasesHash) throw new Error("Eval cases changed before this backtest started");
|
|
15751
|
+
if (cases.some((entry) => entry.requiresVision !== void 0 && typeof entry.requiresVision !== "boolean")) throw new Error("requiresVision must be a boolean");
|
|
15752
|
+
const resolved = await source.read("target-content", {
|
|
15753
|
+
skillName: input.skillName,
|
|
15754
|
+
target: input.target
|
|
15755
|
+
}, input.signal);
|
|
15756
|
+
const run = await source.mutate("run-start", {
|
|
15757
|
+
skillName: input.skillName,
|
|
15758
|
+
target: resolved.target,
|
|
15759
|
+
caseIds: cases.map((entry) => entry.id),
|
|
15760
|
+
...input.expectedCasesHash === void 0 ? {} : { expectedCasesHash: input.expectedCasesHash },
|
|
15761
|
+
...input.route === void 0 ? {} : { model: input.route }
|
|
15762
|
+
}, input.signal);
|
|
15763
|
+
this.background("skill backtest", async (deadline) => {
|
|
15764
|
+
const controller = new AbortController();
|
|
15765
|
+
const abort = () => controller.abort(deadline.aborted ? deadline.reason : input.signal?.reason);
|
|
15766
|
+
deadline.addEventListener("abort", abort, { once: true });
|
|
15767
|
+
input.signal?.addEventListener("abort", abort, { once: true });
|
|
15768
|
+
if (deadline.aborted || input.signal?.aborted) abort();
|
|
15769
|
+
const signal = controller.signal;
|
|
15770
|
+
let checking = false;
|
|
15771
|
+
const watcher = setInterval(() => {
|
|
15772
|
+
if (checking || signal.aborted) return;
|
|
15773
|
+
checking = true;
|
|
15774
|
+
source.read("run", { id: run.id }).then((current) => {
|
|
15775
|
+
if (current.status !== "running") controller.abort(new Error(current.error ?? "Backtest interrupted"));
|
|
15776
|
+
}).catch((error) => controller.abort(error)).finally(() => {
|
|
15777
|
+
checking = false;
|
|
15287
15778
|
});
|
|
15288
|
-
}
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
|
|
15779
|
+
}, 1e3);
|
|
15780
|
+
watcher.unref?.();
|
|
15781
|
+
try {
|
|
15782
|
+
await abortable(signal, async () => {
|
|
15783
|
+
const release = await this.backtestSlot(signal);
|
|
15784
|
+
try {
|
|
15785
|
+
for (const entry of cases.filter((entry) => entry.requiresVision === true)) {
|
|
15786
|
+
signal.throwIfAborted();
|
|
15787
|
+
await source.mutate("run-progress", {
|
|
15788
|
+
id: run.id,
|
|
15789
|
+
result: {
|
|
15790
|
+
caseId: entry.id,
|
|
15791
|
+
title: entry.title,
|
|
15792
|
+
verdict: "error",
|
|
15793
|
+
score: 0,
|
|
15794
|
+
unmeasuredReason: "requires-vision",
|
|
15795
|
+
checks: [],
|
|
15796
|
+
steps: [],
|
|
15797
|
+
answer: "",
|
|
15798
|
+
error: "Not measured: this case requires image input, which text-only backtests cannot receive."
|
|
15799
|
+
}
|
|
15800
|
+
}, signal);
|
|
15801
|
+
}
|
|
15802
|
+
const queue = cases.filter((entry) => entry.requiresVision !== true);
|
|
15803
|
+
if (queue.length > 0) await abortable(signal, () => this.host.runTask(workspace, signal, async (task) => {
|
|
15804
|
+
const worker = async () => {
|
|
15805
|
+
for (let entry = queue.shift(); entry !== void 0; entry = queue.shift()) {
|
|
15806
|
+
signal.throwIfAborted();
|
|
15807
|
+
const result = await this.runCase(task, entry, input.skillName, resolved.content, signal);
|
|
15808
|
+
signal.throwIfAborted();
|
|
15809
|
+
await source.mutate("run-progress", {
|
|
15810
|
+
id: run.id,
|
|
15811
|
+
result
|
|
15812
|
+
}, signal);
|
|
15813
|
+
}
|
|
15814
|
+
};
|
|
15815
|
+
await Promise.all(Array.from({ length: Math.min(BACKTEST_CONCURRENCY, queue.length) }, worker));
|
|
15816
|
+
}, input.route));
|
|
15293
15817
|
signal.throwIfAborted();
|
|
15294
|
-
await source.mutate("run-
|
|
15818
|
+
await source.mutate("run-finish", {
|
|
15295
15819
|
id: run.id,
|
|
15296
|
-
|
|
15297
|
-
});
|
|
15820
|
+
status: "completed"
|
|
15821
|
+
}, signal);
|
|
15822
|
+
} finally {
|
|
15823
|
+
release();
|
|
15298
15824
|
}
|
|
15299
|
-
};
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
|
|
15308
|
-
|
|
15309
|
-
|
|
15310
|
-
|
|
15311
|
-
}
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15825
|
+
});
|
|
15826
|
+
} catch (error) {
|
|
15827
|
+
await source.mutate("run-finish", {
|
|
15828
|
+
id: run.id,
|
|
15829
|
+
status: signal.aborted ? "interrupted" : "failed",
|
|
15830
|
+
error: message(signal.aborted ? signal.reason : error)
|
|
15831
|
+
}).catch(() => void 0);
|
|
15832
|
+
throw error;
|
|
15833
|
+
} finally {
|
|
15834
|
+
clearInterval(watcher);
|
|
15835
|
+
deadline.removeEventListener("abort", abort);
|
|
15836
|
+
input.signal?.removeEventListener("abort", abort);
|
|
15837
|
+
}
|
|
15838
|
+
}, 27e5).finally(() => {
|
|
15839
|
+
this.backtestJobs--;
|
|
15840
|
+
});
|
|
15841
|
+
return { runId: run.id };
|
|
15842
|
+
} catch (error) {
|
|
15843
|
+
this.backtestJobs--;
|
|
15844
|
+
throw error;
|
|
15845
|
+
}
|
|
15316
15846
|
}
|
|
15317
15847
|
/**
|
|
15318
15848
|
* Execute and grade one case. A run that fails (an output limit, a missing
|
|
@@ -15326,9 +15856,15 @@ var SkillEvolution = class {
|
|
|
15326
15856
|
name: skillName,
|
|
15327
15857
|
...content === void 0 ? {} : { content }
|
|
15328
15858
|
}), SKILL_EXECUTOR_SCHEMA, SKILL_EXECUTOR_PERSONA, signal)).structured);
|
|
15329
|
-
|
|
15859
|
+
signal.throwIfAborted();
|
|
15860
|
+
const judged = await this.host.skillTask(task, `Mnemon skill judge: ${entry.title}`.slice(0, 120), skillJudgePrompt(entry, executed.steps, executed.answer), SKILL_JUDGE_SCHEMA, SKILL_JUDGE_PERSONA, signal);
|
|
15861
|
+
const grade = gradeCase(entry, judged.structured);
|
|
15862
|
+
const raw = judged.structured;
|
|
15863
|
+
const checks = Array.isArray(raw?.checks) ? raw.checks : [];
|
|
15864
|
+
const fullyGraded = checks.length === entry.checks.length && entry.checks.every((_check, index) => checks.filter((check) => check?.index === index + 1 && typeof check.pass === "boolean").length === 1);
|
|
15330
15865
|
return {
|
|
15331
15866
|
caseId: entry.id,
|
|
15867
|
+
fullyGraded,
|
|
15332
15868
|
title: entry.title,
|
|
15333
15869
|
verdict: grade.verdict,
|
|
15334
15870
|
score: grade.score,
|
|
@@ -15438,6 +15974,7 @@ var SkillEvolution = class {
|
|
|
15438
15974
|
};
|
|
15439
15975
|
}
|
|
15440
15976
|
dispose() {
|
|
15977
|
+
this.automatic.dispose();
|
|
15441
15978
|
this.disposed = true;
|
|
15442
15979
|
for (const state of this.sessions.values()) if (state.timer !== void 0) clearTimeout(state.timer);
|
|
15443
15980
|
this.sessions.clear();
|
|
@@ -15472,7 +16009,7 @@ function skillsSourceFor(runtime, agent) {
|
|
|
15472
16009
|
}
|
|
15473
16010
|
//#endregion
|
|
15474
16011
|
//#region src/host/rpc.ts
|
|
15475
|
-
function object$
|
|
16012
|
+
function object$5(value) {
|
|
15476
16013
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("payload must be an object");
|
|
15477
16014
|
return value;
|
|
15478
16015
|
}
|
|
@@ -15668,7 +16205,7 @@ async function assisted(runtime, lifecycle, typeId, operation, input, signal, ta
|
|
|
15668
16205
|
...signal === void 0 ? {} : { signal }
|
|
15669
16206
|
});
|
|
15670
16207
|
return {
|
|
15671
|
-
...object$
|
|
16208
|
+
...object$5(result.value),
|
|
15672
16209
|
revision: result.revision
|
|
15673
16210
|
};
|
|
15674
16211
|
} finally {
|
|
@@ -15783,6 +16320,7 @@ async function skillAssistance(runtime, lifecycle, operation, input, signal) {
|
|
|
15783
16320
|
if (guidance !== void 0 && guidance.length > 4e3) throw new Error("guidance is too long (max 4000 characters)");
|
|
15784
16321
|
const draftRoute = modelRoute(input);
|
|
15785
16322
|
return skills.draft(source, workspaceRoot, {
|
|
16323
|
+
...input.backtest === true ? { backtest: true } : {},
|
|
15786
16324
|
...suggestionId === void 0 ? {} : { suggestionId },
|
|
15787
16325
|
...skill === void 0 ? {} : { skill },
|
|
15788
16326
|
...draftId === void 0 ? {} : { draftId },
|
|
@@ -15794,7 +16332,7 @@ async function skillAssistance(runtime, lifecycle, operation, input, signal) {
|
|
|
15794
16332
|
case "skill-backtest": {
|
|
15795
16333
|
const skillName = optionalString(input.skillName, "skillName");
|
|
15796
16334
|
if (skillName === void 0) throw new Error("skillName is required");
|
|
15797
|
-
const target = object$
|
|
16335
|
+
const target = object$5(input.target);
|
|
15798
16336
|
if (![
|
|
15799
16337
|
"current",
|
|
15800
16338
|
"draft",
|
|
@@ -15834,7 +16372,7 @@ async function skillAssistance(runtime, lifecycle, operation, input, signal) {
|
|
|
15834
16372
|
function createReadHandler(input, lifecycle, versions) {
|
|
15835
16373
|
return async (endpoint, rawPayload, signal) => {
|
|
15836
16374
|
try {
|
|
15837
|
-
const payload = object$
|
|
16375
|
+
const payload = object$5(rawPayload);
|
|
15838
16376
|
if (endpoint === "versions") {
|
|
15839
16377
|
if (versions === void 0) throw new Error("version checks are unavailable");
|
|
15840
16378
|
return success$1(await versions.check());
|
|
@@ -15855,7 +16393,7 @@ function createReadHandler(input, lifecycle, versions) {
|
|
|
15855
16393
|
if (lifecycle === void 0 || payload.operation !== "agent-search") throw new Error("Read-only Host assistance is unavailable");
|
|
15856
16394
|
const source = (await catalog(runtime, lifecycle)).sources.find((item) => item.sourceInstanceKey === payload.sourceInstanceKey);
|
|
15857
16395
|
if (source === void 0 || !source.assistance.includes("agent-search")) throw new Error("Host assistance is not available for this Source instance");
|
|
15858
|
-
const value = await assisted(runtime, lifecycle, source.sourceTypeId, "agent-search", object$
|
|
16396
|
+
const value = await assisted(runtime, lifecycle, source.sourceTypeId, "agent-search", object$5(payload.input), signal);
|
|
15859
16397
|
return success$1({
|
|
15860
16398
|
revision: (await catalog(runtime)).sources.find((item) => item.sourceInstanceKey === source.sourceInstanceKey)?.revision ?? source.revision,
|
|
15861
16399
|
value
|
|
@@ -15967,9 +16505,9 @@ function createActivationHandler(input) {
|
|
|
15967
16505
|
return async (endpoint, rawPayload, signal) => {
|
|
15968
16506
|
try {
|
|
15969
16507
|
if (endpoint === "source-assistance") {
|
|
15970
|
-
const payload = object$
|
|
16508
|
+
const payload = object$5(rawPayload);
|
|
15971
16509
|
if (payload.operation !== "activation" || payload.confirmed !== true) throw new Error("Unsupported activation operation");
|
|
15972
|
-
const fields = object$
|
|
16510
|
+
const fields = object$5(payload.input);
|
|
15973
16511
|
if (Object.keys(fields).some((key) => key !== "memoryBodyId" && key !== "active") || typeof fields.active !== "boolean" || typeof fields.memoryBodyId !== "string") throw new Error("Activation accepts only a Memory Space id and boolean state");
|
|
15974
16512
|
const runtime = scoped(input, payload);
|
|
15975
16513
|
requireWritable(runtime);
|
|
@@ -15993,7 +16531,7 @@ function createActivationHandler(input) {
|
|
|
15993
16531
|
}
|
|
15994
16532
|
}
|
|
15995
16533
|
if (endpoint !== "body") return badRequest$1("unknown activation endpoint: " + endpoint);
|
|
15996
|
-
const payload = object$
|
|
16534
|
+
const payload = object$5(rawPayload);
|
|
15997
16535
|
const unexpected = Object.keys(payload).filter((field) => !ACTIVATION_FIELDS.has(field));
|
|
15998
16536
|
if (unexpected.length > 0) return badRequest$1("unsupported activation fields: " + unexpected.join(", "));
|
|
15999
16537
|
if (typeof payload.memoryBodyId !== "string" || payload.memoryBodyId.trim() === "") return badRequest$1("memoryBodyId must be a non-empty string");
|
|
@@ -16012,7 +16550,7 @@ function createActivationHandler(input) {
|
|
|
16012
16550
|
function createWriteHandler(input, lifecycle, versions) {
|
|
16013
16551
|
return async (endpoint, rawPayload, signal) => {
|
|
16014
16552
|
try {
|
|
16015
|
-
const payload = object$
|
|
16553
|
+
const payload = object$5(rawPayload);
|
|
16016
16554
|
if (endpoint === "version-update") {
|
|
16017
16555
|
if (versions === void 0) throw new Error("version updates are unavailable");
|
|
16018
16556
|
if (!input.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only (writeEnabled: false)");
|
|
@@ -16048,12 +16586,12 @@ function createWriteHandler(input, lifecycle, versions) {
|
|
|
16048
16586
|
if (source === void 0 || !source.assistance.includes(operation)) throw new Error("Host assistance is not available for this Source instance");
|
|
16049
16587
|
requireLayerOn(runtime, source.sourceTypeId);
|
|
16050
16588
|
if (operation !== "agent-search" && (payload.confirmed !== true || payload.expectedRevision !== source.revision)) throw new Error("Host assistance requires confirmation of the current Source revision");
|
|
16051
|
-
const value = await assisted(runtime, lifecycle, source.sourceTypeId, operation, object$
|
|
16589
|
+
const value = await assisted(runtime, lifecycle, source.sourceTypeId, operation, object$5(payload.input), signal, {
|
|
16052
16590
|
sourceInstanceKey: source.sourceInstanceKey,
|
|
16053
16591
|
expectedRevision: source.revision
|
|
16054
16592
|
});
|
|
16055
16593
|
if (source.sourceTypeId === "runtime") return success$1({
|
|
16056
|
-
revision: object$
|
|
16594
|
+
revision: object$5(value).revision,
|
|
16057
16595
|
value
|
|
16058
16596
|
});
|
|
16059
16597
|
return success$1({
|
|
@@ -16097,7 +16635,7 @@ function createWriteHandler(input, lifecycle, versions) {
|
|
|
16097
16635
|
function createPackHandler(input, nativeDirectoryPicker = () => false) {
|
|
16098
16636
|
return async (endpoint, rawPayload) => {
|
|
16099
16637
|
try {
|
|
16100
|
-
const payload = object$
|
|
16638
|
+
const payload = object$5(rawPayload);
|
|
16101
16639
|
const runtime = scoped(input, payload);
|
|
16102
16640
|
const manager = runtime.graph.packs;
|
|
16103
16641
|
if (endpoint === "target") return success$1({
|
|
@@ -16134,7 +16672,7 @@ function githubAuth(input) {
|
|
|
16134
16672
|
function createSyncHandler(input, onConfigured) {
|
|
16135
16673
|
return async (endpoint, rawPayload, signal) => {
|
|
16136
16674
|
try {
|
|
16137
|
-
const payload = object$
|
|
16675
|
+
const payload = object$5(rawPayload);
|
|
16138
16676
|
const runtime = scoped(input, payload);
|
|
16139
16677
|
const sync = runtime.graph.sync;
|
|
16140
16678
|
if (endpoint === "status") return success$1(await sync.status(signal));
|
|
@@ -16207,7 +16745,7 @@ function createSyncHandler(input, onConfigured) {
|
|
|
16207
16745
|
function createReviewHandler(input, lifecycle) {
|
|
16208
16746
|
return async (endpoint, rawPayload, signal) => {
|
|
16209
16747
|
try {
|
|
16210
|
-
const payload = object$
|
|
16748
|
+
const payload = object$5(rawPayload);
|
|
16211
16749
|
const ledger = input.reviews;
|
|
16212
16750
|
if (endpoint === "view") return success$1(ledger.view());
|
|
16213
16751
|
if (endpoint === "opinion") {
|
|
@@ -16294,6 +16832,106 @@ function hasNativeDirectoryPicker(ctx) {
|
|
|
16294
16832
|
}
|
|
16295
16833
|
}
|
|
16296
16834
|
//#endregion
|
|
16835
|
+
//#region src/host/embedding-settings.ts
|
|
16836
|
+
const object$4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
16837
|
+
/** Preserve other Host redaction while never returning a legacy inline key. */
|
|
16838
|
+
function publicEmbeddingSettings(value, original, metadata = false) {
|
|
16839
|
+
const config = object$4(value);
|
|
16840
|
+
if (!metadata && config.embedding === void 0) return value;
|
|
16841
|
+
const embedding = object$4(config.embedding);
|
|
16842
|
+
const raw = object$4(object$4(original).embedding);
|
|
16843
|
+
const apiKeyStorage = raw.apiKeyEnv ? "environment" : raw.apiKeyRef ? "credentials" : raw.apiKey ? "legacy" : "none";
|
|
16844
|
+
return {
|
|
16845
|
+
...config,
|
|
16846
|
+
embedding: {
|
|
16847
|
+
...embedding,
|
|
16848
|
+
..."apiKey" in embedding ? { apiKey: "" } : {},
|
|
16849
|
+
...metadata ? {
|
|
16850
|
+
apiKeyStorage,
|
|
16851
|
+
apiKeyConfigured: apiKeyStorage !== "none"
|
|
16852
|
+
} : {}
|
|
16853
|
+
}
|
|
16854
|
+
};
|
|
16855
|
+
}
|
|
16856
|
+
/** Write/verify a new record before referencing it; failures keep the old configuration and key intact. */
|
|
16857
|
+
async function prepareEmbeddingSettings(settings, namespace, operations, revision, store) {
|
|
16858
|
+
const last = operations.findLastIndex((operation) => operation.path.length === 1 && operation.path[0] === "embedding");
|
|
16859
|
+
if (namespace !== "mnemon" || last === -1) return {
|
|
16860
|
+
operations,
|
|
16861
|
+
revision
|
|
16862
|
+
};
|
|
16863
|
+
const operation = operations[last];
|
|
16864
|
+
const edits = operations.filter((entry, index) => index === last || entry.path[0] !== "embedding");
|
|
16865
|
+
if (operation.op === "unset") return {
|
|
16866
|
+
operations: edits,
|
|
16867
|
+
revision
|
|
16868
|
+
};
|
|
16869
|
+
const view = settings.describe({ redactSecrets: false }).find((candidate) => candidate.ns === namespace);
|
|
16870
|
+
if (view === void 0) throw new Error("Mnemon settings are unavailable");
|
|
16871
|
+
if (revision !== void 0 && revision !== view.revision) throw new Error("Settings changed; reload before saving the embedding connection");
|
|
16872
|
+
const previous = resolveEmbedding(object$4(object$4(view.value).embedding));
|
|
16873
|
+
const input = object$4(operation.value);
|
|
16874
|
+
if (typeof operation.value !== "object" || operation.value === null || Array.isArray(operation.value)) throw new Error("Embedding settings must be an object");
|
|
16875
|
+
if (input.apiKey !== void 0 && typeof input.apiKey !== "string") throw new Error("Embedding API key must be text");
|
|
16876
|
+
if (input.clearApiKey !== void 0 && typeof input.clearApiKey !== "boolean") throw new Error("clearApiKey must be boolean");
|
|
16877
|
+
const { apiKeyStorage: _storage, apiKeyConfigured: _configured, clearApiKey: _clear, ...next } = input;
|
|
16878
|
+
let resolved = resolveEmbedding(next);
|
|
16879
|
+
let rollback;
|
|
16880
|
+
if (input.clearApiKey === true) {
|
|
16881
|
+
next.apiKey = "";
|
|
16882
|
+
next.apiKeyRef = "";
|
|
16883
|
+
next.apiKeyEnv = "";
|
|
16884
|
+
} else if (resolved.apiKeyEnv) {
|
|
16885
|
+
if (resolved.apiKey) throw new Error("Choose an embedding API key or an environment variable, not both");
|
|
16886
|
+
next.apiKey = "";
|
|
16887
|
+
next.apiKeyRef = "";
|
|
16888
|
+
} else {
|
|
16889
|
+
if (resolved.apiKeyRef && resolved.apiKeyRef !== previous.apiKeyRef) throw new Error("Save a key to create an embedding credential reference");
|
|
16890
|
+
const sameEndpoint = resolved.endpoint === previous.endpoint;
|
|
16891
|
+
if (!resolved.apiKey && !sameEndpoint && (previous.apiKeyRef || previous.apiKey)) throw new Error("Changing the embedding endpoint requires a new key, an environment variable, or explicitly removing the saved key");
|
|
16892
|
+
const key = resolved.apiKey || (sameEndpoint ? previous.apiKey : "");
|
|
16893
|
+
if (key) {
|
|
16894
|
+
const credentials = store?.();
|
|
16895
|
+
if (credentials === void 0) throw new Error("DSH credentials storage is unavailable; configure apiKeyEnv instead. The existing connection was not changed");
|
|
16896
|
+
const reference = EMBEDDING_CREDENTIAL_PREFIX + randomUUID();
|
|
16897
|
+
const payload = {
|
|
16898
|
+
version: 1,
|
|
16899
|
+
endpoint: resolved.endpoint,
|
|
16900
|
+
apiKey: key
|
|
16901
|
+
};
|
|
16902
|
+
rollback = async () => {
|
|
16903
|
+
await credentials.deleteRecord(reference);
|
|
16904
|
+
};
|
|
16905
|
+
try {
|
|
16906
|
+
await credentials.modifyRecord(reference, async () => ({
|
|
16907
|
+
kind: "grant",
|
|
16908
|
+
payload
|
|
16909
|
+
}));
|
|
16910
|
+
const record = await credentials.readRecord(reference);
|
|
16911
|
+
if (record?.kind !== "grant" || record.payload?.version !== 1 || record.payload.endpoint !== payload.endpoint || record.payload.apiKey !== key) throw new Error("Credential readback failed");
|
|
16912
|
+
} catch {
|
|
16913
|
+
await rollback().catch(() => {});
|
|
16914
|
+
throw new Error("Could not save and verify the embedding credential; the existing connection was not changed");
|
|
16915
|
+
}
|
|
16916
|
+
next.apiKey = "";
|
|
16917
|
+
next.apiKeyRef = reference;
|
|
16918
|
+
} else if (sameEndpoint && previous.apiKeyRef) {
|
|
16919
|
+
next.apiKey = "";
|
|
16920
|
+
next.apiKeyRef = previous.apiKeyRef;
|
|
16921
|
+
}
|
|
16922
|
+
}
|
|
16923
|
+
resolved = resolveEmbedding(next);
|
|
16924
|
+
if (resolved.apiKey) throw new Error("Embedding settings must not persist a plaintext key");
|
|
16925
|
+
return {
|
|
16926
|
+
operations: edits.map((entry) => entry === operation ? {
|
|
16927
|
+
...entry,
|
|
16928
|
+
value: next
|
|
16929
|
+
} : entry),
|
|
16930
|
+
revision: revision ?? view.revision,
|
|
16931
|
+
...rollback === void 0 ? {} : { rollback }
|
|
16932
|
+
};
|
|
16933
|
+
}
|
|
16934
|
+
//#endregion
|
|
16297
16935
|
//#region src/host/settings.ts
|
|
16298
16936
|
function success(value) {
|
|
16299
16937
|
return {
|
|
@@ -16324,14 +16962,16 @@ function badRequest(message) {
|
|
|
16324
16962
|
function descriptor(settings, namespace) {
|
|
16325
16963
|
const view = settings.describe({ redactSecrets: true }).find((candidate) => candidate.ns === namespace);
|
|
16326
16964
|
if (view === void 0) throw new Error(`${namespace} settings namespace is unavailable`);
|
|
16965
|
+
const raw = namespace === "mnemon" ? settings.describe({ redactSecrets: false }).find((candidate) => candidate.ns === namespace) : void 0;
|
|
16966
|
+
const visible = namespace === "mnemon" ? publicEmbeddingSettings(view.value, raw?.value, true) : view.value;
|
|
16327
16967
|
return {
|
|
16328
16968
|
status: "ready",
|
|
16329
16969
|
value: namespace === "mnemon" && displayModeOf(view.value) === "buildin" ? {
|
|
16330
|
-
...object$3(
|
|
16970
|
+
...object$3(visible),
|
|
16331
16971
|
displayMode: "builtin"
|
|
16332
|
-
} :
|
|
16333
|
-
base: view.base,
|
|
16334
|
-
user: view.user,
|
|
16972
|
+
} : visible,
|
|
16973
|
+
base: namespace === "mnemon" ? publicEmbeddingSettings(view.base, raw?.base) : view.base,
|
|
16974
|
+
user: namespace === "mnemon" ? publicEmbeddingSettings(view.user, raw?.user) : view.user,
|
|
16335
16975
|
revision: view.revision,
|
|
16336
16976
|
writable: settings.writable,
|
|
16337
16977
|
mode: "host",
|
|
@@ -16408,7 +17048,7 @@ function mutablePath(namespace, path) {
|
|
|
16408
17048
|
if (path.length === 4 && path[0] === "memoryTopology" && path[1] === "layers" && path[3] === "enabled") return MEMORY_LAYER_ID.test(path[2]);
|
|
16409
17049
|
return INTERACTION_PATHS.some((allowed) => allowed.length === path.length && allowed.every((segment, index) => segment === path[index]));
|
|
16410
17050
|
}
|
|
16411
|
-
function createSettingsHandler(settings) {
|
|
17051
|
+
function createSettingsHandler(settings, credentials) {
|
|
16412
17052
|
return async (endpoint, rawPayload) => {
|
|
16413
17053
|
let namespace = MNEMON_SETTINGS_NAMESPACE;
|
|
16414
17054
|
try {
|
|
@@ -16435,7 +17075,13 @@ function createSettingsHandler(settings) {
|
|
|
16435
17075
|
};
|
|
16436
17076
|
});
|
|
16437
17077
|
const revision = payload.expectedRevision === void 0 ? void 0 : Number(payload.expectedRevision);
|
|
16438
|
-
await settings
|
|
17078
|
+
const prepared = await prepareEmbeddingSettings(settings, namespace, ops, revision, credentials);
|
|
17079
|
+
try {
|
|
17080
|
+
await settings.mutate(namespace, prepared.operations, prepared.revision);
|
|
17081
|
+
} catch (error) {
|
|
17082
|
+
await prepared.rollback?.().catch(() => {});
|
|
17083
|
+
throw error;
|
|
17084
|
+
}
|
|
16439
17085
|
if (namespace === "mnemon") await migrateLegacyDisplayMode(settings);
|
|
16440
17086
|
return success(descriptor(settings, namespace));
|
|
16441
17087
|
} catch (error) {
|
|
@@ -16443,8 +17089,8 @@ function createSettingsHandler(settings) {
|
|
|
16443
17089
|
}
|
|
16444
17090
|
};
|
|
16445
17091
|
}
|
|
16446
|
-
function registerSettingsRpc(connection, settings) {
|
|
16447
|
-
const handler = createSettingsHandler(settings);
|
|
17092
|
+
function registerSettingsRpc(connection, settings, credentials) {
|
|
17093
|
+
const handler = createSettingsHandler(settings, credentials);
|
|
16448
17094
|
connection.rpc.handle(MNEMON_SETTINGS_CHANNEL, handler);
|
|
16449
17095
|
return handler;
|
|
16450
17096
|
}
|
|
@@ -19486,9 +20132,11 @@ function apply(rawContext, rawConfig = {}) {
|
|
|
19486
20132
|
const skills = new SkillEvolution({
|
|
19487
20133
|
settings: () => skillSettings.get(),
|
|
19488
20134
|
source: (agent) => skillsSourceFor(runtime, agent),
|
|
20135
|
+
taskRoute: (workspace) => lifecycle.skillTaskRoute(workspace),
|
|
19489
20136
|
runTask: (workspaceRoot, signal, operation, route) => lifecycle.runSkillTask(workspaceRoot, signal, operation, route),
|
|
19490
20137
|
skillTask: (parent, label, prompt, schema, persona, signal, _route, maxTokens) => coordinator.skillTask(parent, label, prompt, schema, persona, signal, taskRoute(parent), maxTokens)
|
|
19491
20138
|
});
|
|
20139
|
+
ctx.effect(() => hostSettings.onUpdated(() => skills.settingsChanged()), "dsh-mnemon: automatic skill adoption settings");
|
|
19492
20140
|
lifecycle.useSkillEvolution(skills);
|
|
19493
20141
|
ctx.effect(() => {
|
|
19494
20142
|
const stop = autoBackup.start();
|
|
@@ -19527,7 +20175,7 @@ function apply(rawContext, rawConfig = {}) {
|
|
|
19527
20175
|
pluginCommand: () => dshPluginCommand(ctx.get("profileContext"))
|
|
19528
20176
|
});
|
|
19529
20177
|
const rpc = registerRpc(connection, runtime, lifecycle, versions, () => autoBackup.refresh(), () => hasNativeDirectoryPicker(ctx));
|
|
19530
|
-
const settings = registerSettingsRpc(connection, hostSettings);
|
|
20178
|
+
const settings = registerSettingsRpc(connection, hostSettings, () => ctx.get("credentials") ?? ctx.credentials);
|
|
19531
20179
|
const view = registerViewRpc(connection, runtime, extensions, memoryPlugins, lifecycle, pluginInstallation);
|
|
19532
20180
|
if (Context.is(webContext)) new MnemonRemoteService(webContext, {
|
|
19533
20181
|
...rpc,
|