dsh-mnemon 0.5.37 → 0.5.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bundle.js +1 -1
- package/lib/client.js +436 -110
- package/lib/core.js +1 -1
- package/lib/index.js +570 -66
- package/lib/{runtime-zuuBe8x1.js → runtime-0u6dOiF9.js} +33 -1
- package/lib/{starter-resolution-DYeYvfwr.js → starter-resolution-XVFzvnV0.js} +5 -5
- package/lib/starter.js +1 -1
- package/lib/testing.js +1 -1
- package/lib/types/src/client/locales.d.ts +10 -0
- package/lib/types/src/client/page-client.d.ts +3 -1
- package/lib/types/src/client/source-contracts.d.ts +6 -1
- package/lib/types/src/host/protocol.d.ts +10 -1
- package/package.json +6 -6
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { f as canonicalWorkspacePath, m as withMemoryStorageLock, n as memoryStrategyConfigurationValues, p as workspaceStorageId, r as readMemoryStrategyConfiguration, s as receipt } from "./strategy-configuration-CKP2kRss.js";
|
|
2
|
-
import { a as validateMemoryPluginGraph, i as captureMemoryContributionSnapshot, n as isMemoryCompositionUnavailable, o as prepareMemoryContributions, r as MemoryCompositionGeneration, t as provideMemoryRuntime } from "./runtime-
|
|
2
|
+
import { a as validateMemoryPluginGraph, i as captureMemoryContributionSnapshot, n as isMemoryCompositionUnavailable, o as prepareMemoryContributions, r as MemoryCompositionGeneration, t as provideMemoryRuntime } from "./runtime-0u6dOiF9.js";
|
|
3
3
|
import { DEFAULT_MEMORY_VIEW_BUDGET, MEMORY_CAPABILITIES } from "./contracts.js";
|
|
4
4
|
import { r as defineMemoryPlugin, t as canonicalMemoryJson } from "./definitions-DU7MLBhu.js";
|
|
5
5
|
import { Context } from "@deepseek-ai/cordis";
|
|
@@ -9,7 +9,7 @@ import { resolveEmbedding, resolvePersistenceStrategy, resolveRecallQuality } fr
|
|
|
9
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
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";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
|
-
import { accessSync, chmodSync, closeSync, constants, copyFileSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
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";
|
|
14
14
|
import { strToU8, unzipSync, zipSync } from "fflate";
|
|
15
15
|
import { DOCUMENTS_ACTIVE_LIMIT_BYTES, DOCUMENTS_DIRECTORY, DOCUMENTS_INDEX_FILE, DOCUMENTS_LOCK_FILE, DOCUMENTS_LOCK_STALE_MS, DOCUMENT_FILENAME_PATTERN, parseDocumentIndex, summarizeDocumentIndex } from "dsh-mnemon-source-documents/contracts";
|
|
@@ -374,6 +374,7 @@ function isWorkspaceStorageScope(scope) {
|
|
|
374
374
|
const DEFAULT_IDLE_REVIEW = {
|
|
375
375
|
enabled: true,
|
|
376
376
|
runtimeMemory: true,
|
|
377
|
+
persistWatermark: false,
|
|
377
378
|
provider: "spawn",
|
|
378
379
|
fallback: "spawn",
|
|
379
380
|
agentTeams: "pause",
|
|
@@ -535,6 +536,7 @@ const RuntimeMemorySchema = z.object({
|
|
|
535
536
|
const IdleReviewSchema = z.object({
|
|
536
537
|
enabled: z.boolean().default(DEFAULT_IDLE_REVIEW.enabled),
|
|
537
538
|
runtimeMemory: z.boolean().default(DEFAULT_IDLE_REVIEW.runtimeMemory),
|
|
539
|
+
persistWatermark: z.boolean().default(DEFAULT_IDLE_REVIEW.persistWatermark),
|
|
538
540
|
provider: z.union(["spawn", "fork"]).default(DEFAULT_IDLE_REVIEW.provider),
|
|
539
541
|
fallback: z.union(["spawn", "skip"]).default(DEFAULT_IDLE_REVIEW.fallback),
|
|
540
542
|
agentTeams: z.union(["pause", "scoped"]).default(DEFAULT_IDLE_REVIEW.agentTeams),
|
|
@@ -1257,7 +1259,7 @@ const LOADED_VERSION = manifest$1(PACKAGE_MANIFEST_PATH)?.version;
|
|
|
1257
1259
|
function pluginPackageVersion() {
|
|
1258
1260
|
return LOADED_VERSION ?? "0.0.0";
|
|
1259
1261
|
}
|
|
1260
|
-
const CHECK_TIMEOUT_MS = 1e4;
|
|
1262
|
+
const CHECK_TIMEOUT_MS$1 = 1e4;
|
|
1261
1263
|
const UPDATE_TIMEOUT_MS = 6e5;
|
|
1262
1264
|
/** A check waits this long for an update that is finishing, within the browser's 15 s check deadline. */
|
|
1263
1265
|
const IN_FLIGHT_WAIT_MS = 3e3;
|
|
@@ -1353,7 +1355,7 @@ async function fetchJson(url) {
|
|
|
1353
1355
|
const controller = new AbortController();
|
|
1354
1356
|
const timeout = setTimeout(() => {
|
|
1355
1357
|
controller.abort();
|
|
1356
|
-
}, CHECK_TIMEOUT_MS);
|
|
1358
|
+
}, CHECK_TIMEOUT_MS$1);
|
|
1357
1359
|
try {
|
|
1358
1360
|
const response = await fetch(url, {
|
|
1359
1361
|
signal: controller.signal,
|
|
@@ -1621,7 +1623,7 @@ var VersionUpdateManager = class {
|
|
|
1621
1623
|
let current;
|
|
1622
1624
|
try {
|
|
1623
1625
|
const invocation = resolveMnemonInvocation(command, ["--version"]);
|
|
1624
|
-
current = versionFrom((await resultOrThrow(this.processRunner, invocation.command, invocation.args, CHECK_TIMEOUT_MS, { env: invocation.env })).stdout);
|
|
1626
|
+
current = versionFrom((await resultOrThrow(this.processRunner, invocation.command, invocation.args, CHECK_TIMEOUT_MS$1, { env: invocation.env })).stdout);
|
|
1625
1627
|
} catch {
|
|
1626
1628
|
return { install: {
|
|
1627
1629
|
mode: launcher === void 0 ? "manual" : "npm",
|
|
@@ -1642,7 +1644,7 @@ var VersionUpdateManager = class {
|
|
|
1642
1644
|
...npm.args,
|
|
1643
1645
|
"root",
|
|
1644
1646
|
"--global"
|
|
1645
|
-
], CHECK_TIMEOUT_MS, { env: npm.env })).stdout.trim();
|
|
1647
|
+
], CHECK_TIMEOUT_MS$1, { env: npm.env })).stdout.trim();
|
|
1646
1648
|
if (isAbsolute(globalRoot) && samePath(dirname(dirname(launcher)), join(globalRoot, MNEMON_NPM_PACKAGE))) {
|
|
1647
1649
|
install.hint = "npm";
|
|
1648
1650
|
install.updateCommand = process.execPath;
|
|
@@ -1703,7 +1705,7 @@ var VersionUpdateManager = class {
|
|
|
1703
1705
|
"GOARCH",
|
|
1704
1706
|
"GOHOSTOS",
|
|
1705
1707
|
"GOHOSTARCH"
|
|
1706
|
-
], CHECK_TIMEOUT_MS)).stdout);
|
|
1708
|
+
], CHECK_TIMEOUT_MS$1)).stdout);
|
|
1707
1709
|
const goPath = typeof environment.GOPATH === "string" ? environment.GOPATH.split(delimiter)[0] : void 0;
|
|
1708
1710
|
const bin = typeof environment.GOBIN === "string" && environment.GOBIN !== "" ? environment.GOBIN : goPath === void 0 || goPath === "" ? void 0 : join(goPath, "bin");
|
|
1709
1711
|
if (bin === void 0 || !isAbsolute(bin) || typeof environment.GOOS !== "string" || environment.GOOS !== environment.GOHOSTOS || typeof environment.GOARCH !== "string" || environment.GOARCH !== environment.GOHOSTARCH || realpathSync(join(bin, process.platform === "win32" ? "mnemon.exe" : "mnemon")) !== realCommand) return {
|
|
@@ -1717,7 +1719,7 @@ var VersionUpdateManager = class {
|
|
|
1717
1719
|
"version",
|
|
1718
1720
|
"-m",
|
|
1719
1721
|
command
|
|
1720
|
-
], CHECK_TIMEOUT_MS)).stdout.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((fields) => fields[0] === "path")?.[1] === MNEMON_MODULE) return {
|
|
1722
|
+
], CHECK_TIMEOUT_MS$1)).stdout.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((fields) => fields[0] === "path")?.[1] === MNEMON_MODULE) return {
|
|
1721
1723
|
...current === void 0 ? {} : { current },
|
|
1722
1724
|
install: {
|
|
1723
1725
|
mode: "go",
|
|
@@ -3994,7 +3996,7 @@ function shapeOf(answer) {
|
|
|
3994
3996
|
const listed = keys.length > names.length ? [...names, String(keys.length - names.length) + " other"] : names;
|
|
3995
3997
|
return "HTTP " + String(answer.status) + ", content-type " + (answer.type ?? "none") + ", fields: " + (listed.length === 0 ? "none" : listed.join(", "));
|
|
3996
3998
|
}
|
|
3997
|
-
function failure$
|
|
3999
|
+
function failure$3(action, status, body) {
|
|
3998
4000
|
const code = codeOf(body);
|
|
3999
4001
|
const message = code === void 0 ? action + " failed with HTTP " + String(status) : action + " failed: " + code;
|
|
4000
4002
|
return status === 401 ? new MnemonSyncError("github-unauthorized", message) : new Error(message);
|
|
@@ -4135,7 +4137,7 @@ var MnemonGitHubAuth = class {
|
|
|
4135
4137
|
} catch (error) {
|
|
4136
4138
|
throw new Error("the GitHub device code request could not reach GitHub: " + describeTransportError(error));
|
|
4137
4139
|
}
|
|
4138
|
-
if (answer.status !== 200) throw failure$
|
|
4140
|
+
if (answer.status !== 200) throw failure$3("the GitHub device code request", answer.status, answer.body);
|
|
4139
4141
|
const fields = record$6(answer.body);
|
|
4140
4142
|
const deviceCode = text$1(fields?.device_code);
|
|
4141
4143
|
const userCode = text$1(fields?.user_code);
|
|
@@ -4283,7 +4285,7 @@ var MnemonGitHubAuth = class {
|
|
|
4283
4285
|
headers: this.authorization(token),
|
|
4284
4286
|
...signal === void 0 ? {} : { signal }
|
|
4285
4287
|
}, "listing GitHub repositories");
|
|
4286
|
-
if (status !== 200) throw failure$
|
|
4288
|
+
if (status !== 200) throw failure$3("listing GitHub repositories", status, body);
|
|
4287
4289
|
if (!Array.isArray(body)) throw new Error("the GitHub repository answer was not a list");
|
|
4288
4290
|
for (const entry of body) {
|
|
4289
4291
|
const repository = repositoryOf(entry);
|
|
@@ -4314,7 +4316,7 @@ var MnemonGitHubAuth = class {
|
|
|
4314
4316
|
}),
|
|
4315
4317
|
...signal === void 0 ? {} : { signal }
|
|
4316
4318
|
}, "creating the GitHub repository");
|
|
4317
|
-
if (status !== 201) throw failure$
|
|
4319
|
+
if (status !== 201) throw failure$3("creating the GitHub repository", status, body);
|
|
4318
4320
|
const repository = repositoryOf(body);
|
|
4319
4321
|
if (repository === void 0) throw new Error("the created GitHub repository could not be read back");
|
|
4320
4322
|
return repository;
|
|
@@ -7490,6 +7492,7 @@ var LiveMnemonRuntime = class {
|
|
|
7490
7492
|
githubAuth;
|
|
7491
7493
|
autoBackupView;
|
|
7492
7494
|
closed = false;
|
|
7495
|
+
changeListeners = /* @__PURE__ */ new Set();
|
|
7493
7496
|
config;
|
|
7494
7497
|
storage;
|
|
7495
7498
|
packs;
|
|
@@ -7531,6 +7534,12 @@ var LiveMnemonRuntime = class {
|
|
|
7531
7534
|
applyAutoBackup(graph) {
|
|
7532
7535
|
if (this.autoBackupView !== void 0) graph.sync.useAutoBackup(this.autoBackupView);
|
|
7533
7536
|
}
|
|
7537
|
+
onChanged(listener) {
|
|
7538
|
+
this.changeListeners.add(listener);
|
|
7539
|
+
return () => {
|
|
7540
|
+
this.changeListeners.delete(listener);
|
|
7541
|
+
};
|
|
7542
|
+
}
|
|
7534
7543
|
swap(next) {
|
|
7535
7544
|
if (this.closed) {
|
|
7536
7545
|
next.dispose();
|
|
@@ -7544,6 +7553,7 @@ var LiveMnemonRuntime = class {
|
|
|
7544
7553
|
this.retireGraph(previous);
|
|
7545
7554
|
for (const graph of this.workspaceGraphs.values()) this.retireGraph(graph);
|
|
7546
7555
|
this.workspaceGraphs.clear();
|
|
7556
|
+
for (const listener of this.changeListeners) listener();
|
|
7547
7557
|
}
|
|
7548
7558
|
snapshot() {
|
|
7549
7559
|
this.assertOpen();
|
|
@@ -7568,6 +7578,7 @@ var LiveMnemonRuntime = class {
|
|
|
7568
7578
|
dispose() {
|
|
7569
7579
|
if (this.closed) return;
|
|
7570
7580
|
this.executions.dispose();
|
|
7581
|
+
this.changeListeners.clear();
|
|
7571
7582
|
this.closed = true;
|
|
7572
7583
|
const graphs = /* @__PURE__ */ new Set([
|
|
7573
7584
|
this.current,
|
|
@@ -10158,6 +10169,108 @@ async function assertAutomaticMemoryWrite(host, parent) {
|
|
|
10158
10169
|
return host.writableSourceFor(parent, "memory-spaces", "remember");
|
|
10159
10170
|
}
|
|
10160
10171
|
//#endregion
|
|
10172
|
+
//#region src/host/review-admission.ts
|
|
10173
|
+
const RESULT_TOOL = "mnemon_subagent_result";
|
|
10174
|
+
const CHECK_TIMEOUT_MS = 5e3;
|
|
10175
|
+
/** A no-op challenge through the same mandatory completion channel as review.
|
|
10176
|
+
* Inventory alone cannot see execution-time guards. No errors are classified as
|
|
10177
|
+
* permission failures, and an unavailable checkpoint is never a permanent ban.
|
|
10178
|
+
*/
|
|
10179
|
+
var AutomaticReviewAdmission = class {
|
|
10180
|
+
challenges = /* @__PURE__ */ new Map();
|
|
10181
|
+
controllers = /* @__PURE__ */ new Set();
|
|
10182
|
+
draining = /* @__PURE__ */ new WeakSet();
|
|
10183
|
+
disposed = false;
|
|
10184
|
+
/** Only an unexpired, host-issued challenge for this exact Agent can no-op. */
|
|
10185
|
+
acknowledge(args, execution) {
|
|
10186
|
+
if (typeof args !== "object" || args === null || !("requestId" in args) || typeof args.requestId !== "string") return false;
|
|
10187
|
+
const challenge = this.challenges.get(args.requestId);
|
|
10188
|
+
if (challenge === void 0 || challenge.agent !== execution.agent || challenge.execution !== void 0) return false;
|
|
10189
|
+
execution.signal.throwIfAborted();
|
|
10190
|
+
challenge.execution = execution;
|
|
10191
|
+
return true;
|
|
10192
|
+
}
|
|
10193
|
+
dispose() {
|
|
10194
|
+
this.disposed = true;
|
|
10195
|
+
this.challenges.clear();
|
|
10196
|
+
for (const controller of this.controllers) controller.abort();
|
|
10197
|
+
}
|
|
10198
|
+
async check(agent, signal) {
|
|
10199
|
+
signal.throwIfAborted();
|
|
10200
|
+
const tools = agent.ctx.tools;
|
|
10201
|
+
if (this.disposed || tools?.execute === void 0 || this.draining.has(agent)) return false;
|
|
10202
|
+
const controller = new AbortController();
|
|
10203
|
+
this.controllers.add(controller);
|
|
10204
|
+
const combined = AbortSignal.any([signal, controller.signal]);
|
|
10205
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
10206
|
+
timeout.unref?.();
|
|
10207
|
+
let rejectAbort;
|
|
10208
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
10209
|
+
rejectAbort = () => reject(combined.reason);
|
|
10210
|
+
combined.addEventListener("abort", rejectAbort, { once: true });
|
|
10211
|
+
});
|
|
10212
|
+
const attempt = async (codeMode) => {
|
|
10213
|
+
combined.throwIfAborted();
|
|
10214
|
+
const requestId = randomUUID();
|
|
10215
|
+
const callId = randomUUID();
|
|
10216
|
+
const challenge = {
|
|
10217
|
+
agent,
|
|
10218
|
+
accepted: false
|
|
10219
|
+
};
|
|
10220
|
+
this.challenges.set(requestId, challenge);
|
|
10221
|
+
const stops = [];
|
|
10222
|
+
let executing = false;
|
|
10223
|
+
const cleanup = () => {
|
|
10224
|
+
for (const stop of stops.reverse()) stop();
|
|
10225
|
+
this.draining.delete(agent);
|
|
10226
|
+
};
|
|
10227
|
+
try {
|
|
10228
|
+
stops.push(agent.ctx.on("tools/result", ((execution, result) => {
|
|
10229
|
+
if (execution === challenge.execution) challenge.accepted = result.isError === false;
|
|
10230
|
+
})));
|
|
10231
|
+
stops.push(agent.ctx.on("tools/pre-execute", (async (execution, next) => {
|
|
10232
|
+
const decision = await next();
|
|
10233
|
+
return execution.agent === agent && (execution.callId === callId || execution.rootCallId === callId) && decision.kind === "ask" ? { kind: "cancel" } : decision;
|
|
10234
|
+
}), { prepend: true }));
|
|
10235
|
+
const args = {
|
|
10236
|
+
requestId,
|
|
10237
|
+
result: {}
|
|
10238
|
+
};
|
|
10239
|
+
const pending = tools.execute({
|
|
10240
|
+
callId,
|
|
10241
|
+
agent,
|
|
10242
|
+
signal: combined,
|
|
10243
|
+
name: codeMode ? "run_code" : RESULT_TOOL,
|
|
10244
|
+
arguments: codeMode ? {
|
|
10245
|
+
description: "Check automatic memory review availability",
|
|
10246
|
+
code: `await tools.mnemon_subagent_result(${JSON.stringify(args)})`
|
|
10247
|
+
} : args
|
|
10248
|
+
});
|
|
10249
|
+
executing = true;
|
|
10250
|
+
this.draining.add(agent);
|
|
10251
|
+
const settled = pending.finally(cleanup);
|
|
10252
|
+
const outcome = await Promise.race([settled, aborted]);
|
|
10253
|
+
combined.throwIfAborted();
|
|
10254
|
+
return challenge.execution !== void 0 && challenge.accepted && outcome.isError === false;
|
|
10255
|
+
} finally {
|
|
10256
|
+
this.challenges.delete(requestId);
|
|
10257
|
+
if (!executing) cleanup();
|
|
10258
|
+
}
|
|
10259
|
+
};
|
|
10260
|
+
try {
|
|
10261
|
+
if (await attempt(false)) return true;
|
|
10262
|
+
return tools.get?.("run_code", agent) !== void 0 && await attempt(true);
|
|
10263
|
+
} catch {
|
|
10264
|
+
signal.throwIfAborted();
|
|
10265
|
+
return false;
|
|
10266
|
+
} finally {
|
|
10267
|
+
clearTimeout(timeout);
|
|
10268
|
+
if (rejectAbort !== void 0) combined.removeEventListener("abort", rejectAbort);
|
|
10269
|
+
this.controllers.delete(controller);
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
10272
|
+
};
|
|
10273
|
+
//#endregion
|
|
10161
10274
|
//#region src/host/subagent.ts
|
|
10162
10275
|
function evidenceInsights(evidence) {
|
|
10163
10276
|
return evidence.items.map((item) => {
|
|
@@ -10343,6 +10456,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10343
10456
|
/** Delegated children whose run has ended, newest last, at most FINISHED_CHILDREN. */
|
|
10344
10457
|
finishedChildren = /* @__PURE__ */ new Set();
|
|
10345
10458
|
disposed = false;
|
|
10459
|
+
reviewAdmission = new AutomaticReviewAdmission();
|
|
10346
10460
|
constructor(subagents, runtimeSource, resultRuntime, taskAgentModelResolver, runtimeMaintenanceMaxTokensResolver, runtimeMaintenanceTaskRunner) {
|
|
10347
10461
|
this.subagents = subagents;
|
|
10348
10462
|
this.runtimeSource = runtimeSource;
|
|
@@ -10363,6 +10477,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10363
10477
|
}]
|
|
10364
10478
|
},
|
|
10365
10479
|
execute: async (args, execution) => {
|
|
10480
|
+
if (this.reviewAdmission.acknowledge(args, execution)) return { recorded: true };
|
|
10366
10481
|
if (!isSubagent(execution.agent)) throw new Error("Mnemon subagent result tools are restricted to delegated children");
|
|
10367
10482
|
execution.signal.throwIfAborted();
|
|
10368
10483
|
assertDshOutputValue(RESULT_TOOL_INPUT_SCHEMA, args);
|
|
@@ -10377,6 +10492,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10377
10492
|
}
|
|
10378
10493
|
dispose() {
|
|
10379
10494
|
this.disposed = true;
|
|
10495
|
+
this.reviewAdmission.dispose();
|
|
10380
10496
|
this.resultRequests.clear();
|
|
10381
10497
|
const resumeGuard = this.disposeResumeGuard;
|
|
10382
10498
|
this.disposeResumeGuard = void 0;
|
|
@@ -10922,7 +11038,28 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
10922
11038
|
throw new Error("The Memory Space directory could not be read, so nothing was saved: " + (error instanceof Error ? error.message : String(error)), { cause: error });
|
|
10923
11039
|
}
|
|
10924
11040
|
}
|
|
10925
|
-
|
|
11041
|
+
/** Cheap scheduler admission: no model task when every review write target is unavailable. */
|
|
11042
|
+
canReview(parent) {
|
|
11043
|
+
return this.reviewTools(parent).some((tool) => tool !== "mnemon_document_search");
|
|
11044
|
+
}
|
|
11045
|
+
/** Automatic review admission; explicit user-requested reviews are unchanged. */
|
|
11046
|
+
checkReviewAccess(parent, signal) {
|
|
11047
|
+
return this.reviewAdmission.check(parent, signal);
|
|
11048
|
+
}
|
|
11049
|
+
availableTools(parent, tools) {
|
|
11050
|
+
const registry = parent.ctx?.tools;
|
|
11051
|
+
return typeof registry?.get === "function" ? tools.filter((name) => registry.get(name, parent) !== void 0) : [...tools];
|
|
11052
|
+
}
|
|
11053
|
+
reviewTools(parent) {
|
|
11054
|
+
const config = this.runtimeSource.config;
|
|
11055
|
+
if (!config.writeEnabled) return [];
|
|
11056
|
+
return this.availableTools(parent, REVIEW_TOOLS).filter((tool) => {
|
|
11057
|
+
if (tool === "mnemon_document_search") return true;
|
|
11058
|
+
const type = memoryTool(tool).sourceTypeId;
|
|
11059
|
+
return (tool !== "mnemon_runtime_memory" || config.idleReview.runtimeMemory) && allowsParticipation(config, type, "write", "automatic");
|
|
11060
|
+
});
|
|
11061
|
+
}
|
|
11062
|
+
async review(parent, signal, admission) {
|
|
10926
11063
|
const config = this.runtimeSource.config.idleReview;
|
|
10927
11064
|
if (idleReviewBlockReason(parent, config.agentTeams) !== void 0) return {
|
|
10928
11065
|
delegated: false,
|
|
@@ -10930,6 +11067,13 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
10930
11067
|
summary: "Idle review is paused by the Agent Teams compatibility setting.",
|
|
10931
11068
|
documentIds: []
|
|
10932
11069
|
};
|
|
11070
|
+
const skipped = {
|
|
11071
|
+
delegated: false,
|
|
11072
|
+
action: "skipped",
|
|
11073
|
+
summary: "Idle review has no applicable writable Source target.",
|
|
11074
|
+
documentIds: []
|
|
11075
|
+
};
|
|
11076
|
+
if (!this.canReview(parent)) return skipped;
|
|
10933
11077
|
let preferred = config.provider;
|
|
10934
11078
|
if (preferred === "fork") try {
|
|
10935
11079
|
this.provider("fork");
|
|
@@ -10942,24 +11086,40 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
10942
11086
|
};
|
|
10943
11087
|
preferred = "spawn";
|
|
10944
11088
|
}
|
|
10945
|
-
const
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
11089
|
+
const execution = await this.runtimeSource.executions.workflow(parent, "review", signal);
|
|
11090
|
+
try {
|
|
11091
|
+
if (admission !== void 0) {
|
|
11092
|
+
execution.signal.throwIfAborted();
|
|
11093
|
+
admission();
|
|
11094
|
+
}
|
|
11095
|
+
const view = execution.context.view;
|
|
11096
|
+
const instances = execution.graph.memoryComposition.generation(view.runtimeGeneration).sourceInstances();
|
|
11097
|
+
const tools = this.reviewTools(parent).filter((tool) => {
|
|
11098
|
+
if (tool === "mnemon_document_search") return true;
|
|
11099
|
+
const entry = memoryTool(tool);
|
|
11100
|
+
return view.actionOffers.some((offer) => offer.authority === void 0 && offer.sourceActionId === entry.operation && instances.some((instance) => instance.sourceInstanceKey === offer.sourceInstanceKey && instance.sourceTypeId === entry.sourceTypeId));
|
|
11101
|
+
});
|
|
11102
|
+
const writers = tools.filter((tool) => tool !== "mnemon_document_search");
|
|
11103
|
+
if (writers.length === 0) return skipped;
|
|
11104
|
+
const prompt = preferred === "fork" ? "Review the inherited completed checkpoint now." : reviewCheckpoint(parent.session, config.maxContextChars);
|
|
11105
|
+
const base = reviewPersona(tools.includes("mnemon_runtime_memory"));
|
|
11106
|
+
const persona = preferred === "fork" ? base : base.replaceAll("inherited", "supplied bounded");
|
|
11107
|
+
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);
|
|
11108
|
+
const value = object$6(result.structured);
|
|
11109
|
+
return {
|
|
11110
|
+
delegated: true,
|
|
11111
|
+
runId,
|
|
11112
|
+
provider,
|
|
11113
|
+
summary: typeof value.summary === "string" ? value.summary : "",
|
|
11114
|
+
action: typeof value.action === "string" ? value.action : "failed",
|
|
11115
|
+
memoryBodyIds: strings(value.memoryBodyIds),
|
|
11116
|
+
documentIds: strings(value.documentIds)
|
|
11117
|
+
};
|
|
11118
|
+
} finally {
|
|
11119
|
+
execution.release();
|
|
11120
|
+
}
|
|
10961
11121
|
}
|
|
10962
|
-
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route) {
|
|
11122
|
+
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route, admission) {
|
|
10963
11123
|
if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
|
|
10964
11124
|
const provider = this.provider(preferredProvider);
|
|
10965
11125
|
assertDshOutputSchema(outputSchema);
|
|
@@ -11076,19 +11236,25 @@ This is the only completion channel for this run. Do not finish with a plain-tex
|
|
|
11076
11236
|
model: fixed.model
|
|
11077
11237
|
};
|
|
11078
11238
|
const host = this.resultRuntime;
|
|
11079
|
-
const start = () => startWithContinuationTurns(host, parent, () =>
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11239
|
+
const start = () => startWithContinuationTurns(host, parent, () => {
|
|
11240
|
+
if (operation === "review") {
|
|
11241
|
+
signal.throwIfAborted();
|
|
11242
|
+
admission?.();
|
|
11243
|
+
}
|
|
11244
|
+
return startSubagent(this.subagents, host.agents, provider, {
|
|
11245
|
+
label,
|
|
11246
|
+
prompt: [{
|
|
11247
|
+
type: "text",
|
|
11248
|
+
text: `${delegatedTaskLine(operation)}\n${prompt}`
|
|
11249
|
+
}],
|
|
11250
|
+
parent,
|
|
11251
|
+
signal,
|
|
11252
|
+
...resolvedAgentOptions === void 0 ? {} : { agentOptions: resolvedAgentOptions },
|
|
11253
|
+
maxDepth: 1,
|
|
11254
|
+
toolFilter: { allow: [...this.availableTools(parent, tools), resultToolName] },
|
|
11255
|
+
persona: completionPersona
|
|
11256
|
+
}, host);
|
|
11257
|
+
});
|
|
11092
11258
|
run = operation === "review" ? await startGuardedReview(this.resultRuntime, parent, [...tools, resultToolName], start, (child) => {
|
|
11093
11259
|
reviewChildId = child.id;
|
|
11094
11260
|
}, reviewLayerPolicy()) : guard !== void 0 ? await startGuardedChild(this.resultRuntime, parent, start, guard, {
|
|
@@ -11765,6 +11931,157 @@ var AgentMemoryTurn = class {
|
|
|
11765
11931
|
}
|
|
11766
11932
|
};
|
|
11767
11933
|
//#endregion
|
|
11934
|
+
//#region src/host/idle-review-watermark.ts
|
|
11935
|
+
const MAX_STATE_BYTES = 16384;
|
|
11936
|
+
const MAX_TIMESTAMP = 864e13;
|
|
11937
|
+
function validAttempts(value) {
|
|
11938
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
11939
|
+
}
|
|
11940
|
+
function validTimestamp(value) {
|
|
11941
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= MAX_TIMESTAMP;
|
|
11942
|
+
}
|
|
11943
|
+
/** Messages deliberately omit filesystem paths, session ids and raw input. */
|
|
11944
|
+
function failure$2(error) {
|
|
11945
|
+
const code = error?.code;
|
|
11946
|
+
const detail = code === "EBUSY" ? " (busy)" : typeof code === "string" && /^[A-Z_]+$/u.test(code) ? ` (${code})` : "";
|
|
11947
|
+
return new Error(`Idle review watermark unavailable${detail}. Automatic review is paused. Stop Hosts using this storage root, repair this session's state or lock under state/idle-review, then reopen the session.`, { cause: error });
|
|
11948
|
+
}
|
|
11949
|
+
/** Host-local admission state, deliberately outside Packs and Git sync. */
|
|
11950
|
+
var IdleReviewWatermarkStore = class {
|
|
11951
|
+
sessionId;
|
|
11952
|
+
directory;
|
|
11953
|
+
path;
|
|
11954
|
+
constructor(directory, sessionId) {
|
|
11955
|
+
this.sessionId = sessionId;
|
|
11956
|
+
if (!sessionId || Buffer.byteLength(sessionId) > 4096 || Buffer.byteLength(JSON.stringify(sessionId)) > 16128) throw failure$2(void 0);
|
|
11957
|
+
this.directory = join(resolve(directory), "state", "idle-review");
|
|
11958
|
+
this.path = join(this.directory, createHash("sha256").update(sessionId).digest("hex") + ".json");
|
|
11959
|
+
}
|
|
11960
|
+
load() {
|
|
11961
|
+
let descriptor;
|
|
11962
|
+
try {
|
|
11963
|
+
try {
|
|
11964
|
+
descriptor = fs.openSync(this.path, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
|
|
11965
|
+
} catch (error) {
|
|
11966
|
+
if (error.code === "ENOENT") return {
|
|
11967
|
+
version: 1,
|
|
11968
|
+
sessionId: this.sessionId,
|
|
11969
|
+
attempts: 0,
|
|
11970
|
+
lastReviewAttemptAt: null
|
|
11971
|
+
};
|
|
11972
|
+
throw error;
|
|
11973
|
+
}
|
|
11974
|
+
const info = fs.fstatSync(descriptor);
|
|
11975
|
+
if (!info.isFile() || info.size > MAX_STATE_BYTES) throw new Error("invalid state file");
|
|
11976
|
+
const bytes = Buffer.alloc(16385);
|
|
11977
|
+
let length = 0;
|
|
11978
|
+
while (length < bytes.length) {
|
|
11979
|
+
const count = fs.readSync(descriptor, bytes, length, bytes.length - length, null);
|
|
11980
|
+
if (count === 0) break;
|
|
11981
|
+
length += count;
|
|
11982
|
+
}
|
|
11983
|
+
if (length > MAX_STATE_BYTES) throw new Error("oversized state");
|
|
11984
|
+
const value = JSON.parse(bytes.subarray(0, length).toString("utf8"));
|
|
11985
|
+
if (value === null || typeof value !== "object") throw new Error("invalid state");
|
|
11986
|
+
const state = value;
|
|
11987
|
+
if (state.version !== 1 || state.sessionId !== this.sessionId || !validAttempts(state.attempts) || (state.attempts === 0 ? state.lastReviewAttemptAt !== null : !validTimestamp(state.lastReviewAttemptAt))) throw new Error("invalid state");
|
|
11988
|
+
return {
|
|
11989
|
+
version: 1,
|
|
11990
|
+
sessionId: this.sessionId,
|
|
11991
|
+
attempts: state.attempts,
|
|
11992
|
+
lastReviewAttemptAt: state.lastReviewAttemptAt
|
|
11993
|
+
};
|
|
11994
|
+
} catch (error) {
|
|
11995
|
+
throw failure$2(error);
|
|
11996
|
+
} finally {
|
|
11997
|
+
if (descriptor !== void 0) try {
|
|
11998
|
+
fs.closeSync(descriptor);
|
|
11999
|
+
} catch (error) {
|
|
12000
|
+
throw failure$2(error);
|
|
12001
|
+
}
|
|
12002
|
+
}
|
|
12003
|
+
}
|
|
12004
|
+
/** Preserve already-used in-process quota on opt-in without admitting work. */
|
|
12005
|
+
restore(floor) {
|
|
12006
|
+
if (floor === void 0 || floor.attempts === 0 && floor.lastReviewAttemptAt === null) return this.load();
|
|
12007
|
+
return this.withLock(() => {
|
|
12008
|
+
const saved = this.load();
|
|
12009
|
+
const merged = this.merge(saved, floor);
|
|
12010
|
+
if (merged.attempts !== saved.attempts || merged.lastReviewAttemptAt !== saved.lastReviewAttemptAt) this.write(merged);
|
|
12011
|
+
return merged;
|
|
12012
|
+
});
|
|
12013
|
+
}
|
|
12014
|
+
reserve(policy) {
|
|
12015
|
+
if (!validTimestamp(policy.now) || !validAttempts(policy.maxPerSession) || !validAttempts(policy.minIntervalMs)) throw failure$2(void 0);
|
|
12016
|
+
return this.withLock(() => {
|
|
12017
|
+
const saved = this.merge(this.load(), policy.floor);
|
|
12018
|
+
if (saved.attempts >= policy.maxPerSession || policy.now - (saved.lastReviewAttemptAt ?? -Infinity) < policy.minIntervalMs) return void 0;
|
|
12019
|
+
const next = {
|
|
12020
|
+
version: 1,
|
|
12021
|
+
sessionId: this.sessionId,
|
|
12022
|
+
attempts: saved.attempts + 1,
|
|
12023
|
+
lastReviewAttemptAt: policy.now
|
|
12024
|
+
};
|
|
12025
|
+
this.write(next);
|
|
12026
|
+
return next;
|
|
12027
|
+
});
|
|
12028
|
+
}
|
|
12029
|
+
merge(saved, floor) {
|
|
12030
|
+
if (floor === void 0) return saved;
|
|
12031
|
+
if (!validAttempts(floor.attempts) || (floor.attempts === 0 ? floor.lastReviewAttemptAt !== null : !validTimestamp(floor.lastReviewAttemptAt))) throw failure$2(void 0);
|
|
12032
|
+
const previous = Math.max(saved.lastReviewAttemptAt ?? -Infinity, floor.lastReviewAttemptAt ?? -Infinity);
|
|
12033
|
+
return {
|
|
12034
|
+
version: 1,
|
|
12035
|
+
sessionId: this.sessionId,
|
|
12036
|
+
attempts: Math.max(saved.attempts, floor.attempts),
|
|
12037
|
+
lastReviewAttemptAt: previous === -Infinity ? null : previous
|
|
12038
|
+
};
|
|
12039
|
+
}
|
|
12040
|
+
write(state) {
|
|
12041
|
+
const temporary = this.path + "." + randomUUID() + ".tmp";
|
|
12042
|
+
try {
|
|
12043
|
+
fs.writeFileSync(temporary, JSON.stringify(state) + "\n", {
|
|
12044
|
+
mode: 384,
|
|
12045
|
+
flag: "wx"
|
|
12046
|
+
});
|
|
12047
|
+
fs.renameSync(temporary, this.path);
|
|
12048
|
+
} finally {
|
|
12049
|
+
fs.rmSync(temporary, { force: true });
|
|
12050
|
+
}
|
|
12051
|
+
}
|
|
12052
|
+
/**
|
|
12053
|
+
* One bounded synchronous read/check/write critical section. Exclusive mkdir
|
|
12054
|
+
* protects independent Hosts too; contention fails closed without stale-lock
|
|
12055
|
+
* takeover. Never hold this lock while starting or awaiting model work.
|
|
12056
|
+
*/
|
|
12057
|
+
withLock(operation) {
|
|
12058
|
+
const lock = this.path + ".lock";
|
|
12059
|
+
let held = false;
|
|
12060
|
+
try {
|
|
12061
|
+
fs.mkdirSync(this.directory, {
|
|
12062
|
+
recursive: true,
|
|
12063
|
+
mode: 448
|
|
12064
|
+
});
|
|
12065
|
+
try {
|
|
12066
|
+
fs.mkdirSync(lock, { mode: 448 });
|
|
12067
|
+
} catch (error) {
|
|
12068
|
+
if (error.code === "EEXIST") throw Object.assign(/* @__PURE__ */ new Error("busy"), { code: "EBUSY" });
|
|
12069
|
+
throw error;
|
|
12070
|
+
}
|
|
12071
|
+
held = true;
|
|
12072
|
+
return operation();
|
|
12073
|
+
} catch (error) {
|
|
12074
|
+
throw failure$2(error);
|
|
12075
|
+
} finally {
|
|
12076
|
+
if (held) try {
|
|
12077
|
+
fs.rmdirSync(lock);
|
|
12078
|
+
} catch (error) {
|
|
12079
|
+
throw failure$2(error);
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
}
|
|
12083
|
+
};
|
|
12084
|
+
//#endregion
|
|
11768
12085
|
//#region src/host/lifecycle.ts
|
|
11769
12086
|
function modelService(value) {
|
|
11770
12087
|
if (typeof value !== "object" || value === null || !("currentSelection" in value) || typeof value.currentSelection !== "function") return void 0;
|
|
@@ -11858,6 +12175,7 @@ var MnemonAgentLifecycle = class {
|
|
|
11858
12175
|
config;
|
|
11859
12176
|
counters;
|
|
11860
12177
|
memoryTurn;
|
|
12178
|
+
runtimeDirectory;
|
|
11861
12179
|
primePending = true;
|
|
11862
12180
|
startSource;
|
|
11863
12181
|
guidedTurns = /* @__PURE__ */ new Set();
|
|
@@ -11874,10 +12192,21 @@ var MnemonAgentLifecycle = class {
|
|
|
11874
12192
|
idleReviewTimer;
|
|
11875
12193
|
reviewController;
|
|
11876
12194
|
reviewRunning = false;
|
|
12195
|
+
reviewAccessBlocked = false;
|
|
11877
12196
|
lastReviewAt;
|
|
11878
12197
|
lastReviewAction;
|
|
11879
12198
|
lastReviewScore;
|
|
11880
12199
|
lastReviewDocumentIds;
|
|
12200
|
+
disposed = false;
|
|
12201
|
+
reviewEpoch = 0;
|
|
12202
|
+
observedReviewPolicy;
|
|
12203
|
+
watermarkEnabled = false;
|
|
12204
|
+
/** Only this Agent's observed usage, attributed to the graph that composed each checkpoint. */
|
|
12205
|
+
reviewUsage = /* @__PURE__ */ new Map();
|
|
12206
|
+
watermarkKey;
|
|
12207
|
+
watermarkStore;
|
|
12208
|
+
watermarkError;
|
|
12209
|
+
restoredAttempts = 0;
|
|
11881
12210
|
idleReviewAttempts = 0;
|
|
11882
12211
|
lastReviewAttemptAt;
|
|
11883
12212
|
lastReviewFailure;
|
|
@@ -11885,15 +12214,18 @@ var MnemonAgentLifecycle = class {
|
|
|
11885
12214
|
lastAt;
|
|
11886
12215
|
lastError;
|
|
11887
12216
|
composedTurn;
|
|
11888
|
-
constructor(agent, coordinator, config, counters, source, memoryTurn) {
|
|
12217
|
+
constructor(agent, coordinator, config, counters, source, memoryTurn, runtimeDirectory) {
|
|
11889
12218
|
this.agent = agent;
|
|
11890
12219
|
this.coordinator = coordinator;
|
|
11891
12220
|
this.config = config;
|
|
11892
12221
|
this.counters = counters;
|
|
11893
12222
|
this.memoryTurn = memoryTurn;
|
|
12223
|
+
this.runtimeDirectory = runtimeDirectory;
|
|
11894
12224
|
this.startSource = source;
|
|
11895
12225
|
}
|
|
11896
12226
|
start() {
|
|
12227
|
+
this.restoreWatermark();
|
|
12228
|
+
this.observedReviewPolicy = this.reviewPolicy();
|
|
11897
12229
|
const disposers = [
|
|
11898
12230
|
this.agent.ctx.on("session/event", ((session, event) => this.sessionEvent(session, event))),
|
|
11899
12231
|
this.agent.ctx.on("system-prompt/assemble", ((assembly, context, next) => this.assemblePrompt(assembly, context, next))),
|
|
@@ -11901,12 +12233,14 @@ var MnemonAgentLifecycle = class {
|
|
|
11901
12233
|
this.agent.ctx.on("agent/turn-stopping", ((payload) => this.finishTurn(payload)))
|
|
11902
12234
|
];
|
|
11903
12235
|
return () => {
|
|
12236
|
+
this.disposed = true;
|
|
11904
12237
|
this.releaseView();
|
|
11905
12238
|
this.cancelIdleReview(true);
|
|
11906
12239
|
for (const dispose of disposers.reverse()) dispose();
|
|
11907
12240
|
};
|
|
11908
12241
|
}
|
|
11909
12242
|
snapshot() {
|
|
12243
|
+
this.restoreWatermark();
|
|
11910
12244
|
return {
|
|
11911
12245
|
sessionId: this.agent.id,
|
|
11912
12246
|
status: this.agent.status,
|
|
@@ -11918,8 +12252,14 @@ var MnemonAgentLifecycle = class {
|
|
|
11918
12252
|
reviewRunning: this.reviewRunning,
|
|
11919
12253
|
reviewActivity: this.reviewActivity(),
|
|
11920
12254
|
idleReviewAttempts: this.idleReviewAttempts,
|
|
11921
|
-
...
|
|
11922
|
-
|
|
12255
|
+
...this.config.idleReview.persistWatermark ? { idleReviewWatermark: {
|
|
12256
|
+
state: this.watermarkError === void 0 ? "ready" : "error",
|
|
12257
|
+
restoredAttempts: this.restoredAttempts,
|
|
12258
|
+
maxPerSession: this.config.idleReview.maxPerSession,
|
|
12259
|
+
...this.watermarkError === void 0 ? {} : { error: this.watermarkError }
|
|
12260
|
+
} } : {},
|
|
12261
|
+
...idleReviewBlockReason(this.agent, this.config.idleReview.agentTeams) !== void 0 ? { idleReviewBlocked: "agent-team" } : this.reviewAccessBlocked ? { idleReviewBlocked: "tool-access" } : {},
|
|
12262
|
+
...this.lastReviewAttemptAt === void 0 ? {} : { nextReviewAt: new Date(Math.min(864e13, this.lastReviewAttemptAt + this.config.idleReview.minIntervalMs)).toISOString() },
|
|
11923
12263
|
...this.lastReviewFailure === void 0 ? {} : { lastReviewFailure: this.lastReviewFailure },
|
|
11924
12264
|
lastPhase: this.lastPhase,
|
|
11925
12265
|
...this.lastReviewAt === void 0 ? {} : { lastReviewAt: this.lastReviewAt },
|
|
@@ -12042,15 +12382,17 @@ var MnemonAgentLifecycle = class {
|
|
|
12042
12382
|
scheduleIdleReview(turn) {
|
|
12043
12383
|
this.cancelIdleReview(true);
|
|
12044
12384
|
this.recordComposedTurn(turn);
|
|
12045
|
-
if (!this.idleReviewAllowed()) return;
|
|
12385
|
+
if (!this.restoreWatermark() || !this.idleReviewAllowed()) return;
|
|
12046
12386
|
const activity = this.ensureTurnActivity(turn);
|
|
12047
12387
|
const tools = completedToolActivity(this.agent.session.snapshotEvents(), turn);
|
|
12048
12388
|
activity.toolCallCount = tools.count;
|
|
12049
12389
|
activity.toolNames = tools.names;
|
|
12050
12390
|
if (!this.reviewActivity().eligible || !this.reviewAdmitted(turn)) return;
|
|
12391
|
+
const epoch = this.reviewEpoch;
|
|
12392
|
+
const policy = this.reviewPolicy();
|
|
12051
12393
|
this.idleReviewTimer = setTimeout(() => {
|
|
12052
12394
|
this.idleReviewTimer = void 0;
|
|
12053
|
-
if (!this.idleReviewAllowed() || this.reviewRunning) return;
|
|
12395
|
+
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed() || this.reviewRunning) return;
|
|
12054
12396
|
if (this.lastReviewAttemptAt !== void 0 && Date.now() < this.lastReviewAttemptAt + this.config.idleReview.minIntervalMs) {
|
|
12055
12397
|
this.scheduleIdleReview(turn);
|
|
12056
12398
|
return;
|
|
@@ -12058,7 +12400,73 @@ var MnemonAgentLifecycle = class {
|
|
|
12058
12400
|
if (this.agent.status !== "idle") return;
|
|
12059
12401
|
if (!this.agent.session.snapshotEvents().some((event) => event.type === "turn/end" && eventTurn(event) === turn) || !this.reviewActivity().eligible || !this.reviewAdmitted(turn)) return;
|
|
12060
12402
|
this.runIdleReview();
|
|
12061
|
-
}, Math.max(this.config.idleReviewMs, (this.lastReviewAttemptAt ?? -Infinity) + this.config.idleReview.minIntervalMs - Date.now()));
|
|
12403
|
+
}, Math.min(2147483647, Math.max(this.config.idleReviewMs, (this.lastReviewAttemptAt ?? -Infinity) + this.config.idleReview.minIntervalMs - Date.now())));
|
|
12404
|
+
}
|
|
12405
|
+
/** Resolve only the graph actually serving this parent, never a selected View or startup cwd. */
|
|
12406
|
+
restoreWatermark() {
|
|
12407
|
+
if (!this.config.idleReview.persistWatermark) {
|
|
12408
|
+
this.watermarkEnabled = false;
|
|
12409
|
+
return true;
|
|
12410
|
+
}
|
|
12411
|
+
try {
|
|
12412
|
+
const directory = this.runtimeDirectory?.();
|
|
12413
|
+
if (directory === void 0 || directory.trim() === "") throw new Error("runtime storage unavailable");
|
|
12414
|
+
const key = JSON.stringify([resolve(directory), this.agent.id]);
|
|
12415
|
+
if (this.watermarkKey === key && this.watermarkEnabled) return this.watermarkError === void 0;
|
|
12416
|
+
const sameRoot = this.watermarkKey === key;
|
|
12417
|
+
this.watermarkEnabled = true;
|
|
12418
|
+
this.watermarkKey = key;
|
|
12419
|
+
this.watermarkStore = new IdleReviewWatermarkStore(directory, this.agent.id);
|
|
12420
|
+
this.watermarkError = void 0;
|
|
12421
|
+
this.restoredAttempts = 0;
|
|
12422
|
+
const floor = this.reviewUsage.get(resolve(directory));
|
|
12423
|
+
if (!sameRoot) {
|
|
12424
|
+
this.idleReviewAttempts = floor?.attempts ?? 0;
|
|
12425
|
+
this.lastReviewAttemptAt = floor?.lastReviewAttemptAt ?? void 0;
|
|
12426
|
+
}
|
|
12427
|
+
const state = this.watermarkStore.restore(floor);
|
|
12428
|
+
this.restoredAttempts = state.attempts;
|
|
12429
|
+
this.idleReviewAttempts = state.attempts;
|
|
12430
|
+
this.lastReviewAttemptAt = state.lastReviewAttemptAt ?? void 0;
|
|
12431
|
+
this.reviewUsage.set(resolve(directory), state);
|
|
12432
|
+
return true;
|
|
12433
|
+
} catch (error) {
|
|
12434
|
+
this.pauseWatermark(error);
|
|
12435
|
+
return false;
|
|
12436
|
+
}
|
|
12437
|
+
}
|
|
12438
|
+
configurationChanged() {
|
|
12439
|
+
const policy = this.reviewPolicy();
|
|
12440
|
+
if (policy !== this.observedReviewPolicy) {
|
|
12441
|
+
this.cancelIdleReview(true);
|
|
12442
|
+
this.reviewAccessBlocked = false;
|
|
12443
|
+
}
|
|
12444
|
+
this.observedReviewPolicy = policy;
|
|
12445
|
+
this.restoreWatermark();
|
|
12446
|
+
}
|
|
12447
|
+
pauseWatermark(error) {
|
|
12448
|
+
const message = error instanceof Error && error.message.startsWith("Idle review watermark unavailable") ? error.message : "Idle review watermark unavailable. Automatic review is paused. Stop Hosts using this storage root, repair this session's state or lock under state/idle-review, then reopen the session.";
|
|
12449
|
+
this.watermarkError = message;
|
|
12450
|
+
}
|
|
12451
|
+
/** Policy/root changes invalidate an already queued checkpoint instead of catching up. */
|
|
12452
|
+
reviewPolicy() {
|
|
12453
|
+
try {
|
|
12454
|
+
return JSON.stringify([
|
|
12455
|
+
this.config.idleReview.persistWatermark ? this.runtimeDirectory?.() : void 0,
|
|
12456
|
+
this.config.lifecycleEnabled,
|
|
12457
|
+
this.config.writeEnabled,
|
|
12458
|
+
this.config.storageScope,
|
|
12459
|
+
this.config.dataDir,
|
|
12460
|
+
this.config.writebackMode,
|
|
12461
|
+
this.config.idleReviewMs,
|
|
12462
|
+
this.config.idleReview,
|
|
12463
|
+
this.config.taskAgentModel,
|
|
12464
|
+
mainStrategyTypeId(this.config)
|
|
12465
|
+
]);
|
|
12466
|
+
} catch (error) {
|
|
12467
|
+
if (this.config.idleReview.persistWatermark) this.pauseWatermark(error);
|
|
12468
|
+
return;
|
|
12469
|
+
}
|
|
12062
12470
|
}
|
|
12063
12471
|
/** Which Strategy composed the turn, while its View is still pinned, and whether it declares idle review. */
|
|
12064
12472
|
recordComposedTurn(turn) {
|
|
@@ -12069,7 +12477,8 @@ var MnemonAgentLifecycle = class {
|
|
|
12069
12477
|
this.composedTurn = {
|
|
12070
12478
|
turn,
|
|
12071
12479
|
typeId: strategy.typeId,
|
|
12072
|
-
idleReview: strategyDrives(strategy, "idle-review")
|
|
12480
|
+
idleReview: strategyDrives(strategy, "idle-review"),
|
|
12481
|
+
directory: pinned.graph.directory
|
|
12073
12482
|
};
|
|
12074
12483
|
} else if (this.composedTurn?.turn !== turn) this.composedTurn = void 0;
|
|
12075
12484
|
}
|
|
@@ -12086,19 +12495,63 @@ var MnemonAgentLifecycle = class {
|
|
|
12086
12495
|
if (this.memoryTurn === void 0) return strategyDrives({ typeId }, "idle-review");
|
|
12087
12496
|
return this.composedTurn?.typeId === typeId && this.composedTurn.idleReview;
|
|
12088
12497
|
}
|
|
12089
|
-
idleReviewAllowed() {
|
|
12090
|
-
return this.config.lifecycleEnabled && this.config.writeEnabled && this.config.writebackMode === "guided" && this.config.idleReview.enabled && this.idleReviewAttempts < this.config.idleReview.maxPerSession && idleReviewBlockReason(this.agent, this.config.idleReview.agentTeams) === void 0 && this.strategyDrivesIdleReview();
|
|
12498
|
+
idleReviewAllowed(reserved = false) {
|
|
12499
|
+
return !this.disposed && (!this.config.idleReview.persistWatermark || this.watermarkError === void 0) && this.config.lifecycleEnabled && this.config.writeEnabled && this.config.writebackMode === "guided" && this.config.idleReview.enabled && (reserved || this.idleReviewAttempts < this.config.idleReview.maxPerSession) && idleReviewBlockReason(this.agent, this.config.idleReview.agentTeams) === void 0 && this.strategyDrivesIdleReview() && this.coordinator.canReview(this.agent);
|
|
12091
12500
|
}
|
|
12092
12501
|
async runIdleReview() {
|
|
12093
12502
|
const controller = new AbortController();
|
|
12094
12503
|
const triggeredScore = this.reviewActivity().score;
|
|
12095
|
-
this.
|
|
12096
|
-
|
|
12504
|
+
const epoch = this.reviewEpoch;
|
|
12505
|
+
const policy = this.reviewPolicy();
|
|
12097
12506
|
this.reviewRunning = true;
|
|
12098
12507
|
this.reviewController = controller;
|
|
12099
|
-
this.mark("review");
|
|
12100
12508
|
try {
|
|
12101
|
-
const
|
|
12509
|
+
const available = await this.coordinator.checkReviewAccess(this.agent, controller.signal);
|
|
12510
|
+
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed()) controller.abort(/* @__PURE__ */ new Error("Automatic idle review admission changed"));
|
|
12511
|
+
controller.signal.throwIfAborted();
|
|
12512
|
+
this.reviewAccessBlocked = !available;
|
|
12513
|
+
if (!available) return;
|
|
12514
|
+
if (this.config.idleReview.persistWatermark) try {
|
|
12515
|
+
if (!this.restoreWatermark() || this.watermarkStore === void 0 || !this.idleReviewAllowed()) return;
|
|
12516
|
+
const reserved = this.watermarkStore.reserve({
|
|
12517
|
+
...this.config.idleReview,
|
|
12518
|
+
now: Date.now(),
|
|
12519
|
+
floor: {
|
|
12520
|
+
attempts: this.idleReviewAttempts,
|
|
12521
|
+
lastReviewAttemptAt: this.lastReviewAttemptAt ?? null
|
|
12522
|
+
}
|
|
12523
|
+
});
|
|
12524
|
+
const state = reserved ?? this.watermarkStore.load();
|
|
12525
|
+
this.idleReviewAttempts = Math.max(this.idleReviewAttempts, state.attempts);
|
|
12526
|
+
this.lastReviewAttemptAt = Math.max(this.lastReviewAttemptAt ?? -Infinity, state.lastReviewAttemptAt ?? -Infinity);
|
|
12527
|
+
if (this.lastReviewAttemptAt === -Infinity) this.lastReviewAttemptAt = void 0;
|
|
12528
|
+
const directory = this.runtimeDirectory?.();
|
|
12529
|
+
if (directory !== void 0) this.reviewUsage.set(resolve(directory), {
|
|
12530
|
+
attempts: this.idleReviewAttempts,
|
|
12531
|
+
lastReviewAttemptAt: this.lastReviewAttemptAt ?? null
|
|
12532
|
+
});
|
|
12533
|
+
if (reserved === void 0) return;
|
|
12534
|
+
} catch (error) {
|
|
12535
|
+
this.pauseWatermark(error);
|
|
12536
|
+
return;
|
|
12537
|
+
}
|
|
12538
|
+
else {
|
|
12539
|
+
this.idleReviewAttempts += 1;
|
|
12540
|
+
this.lastReviewAttemptAt = Date.now();
|
|
12541
|
+
const directory = this.composedTurn?.directory;
|
|
12542
|
+
if (directory !== void 0) {
|
|
12543
|
+
const key = resolve(directory);
|
|
12544
|
+
this.reviewUsage.set(key, {
|
|
12545
|
+
attempts: (this.reviewUsage.get(key)?.attempts ?? 0) + 1,
|
|
12546
|
+
lastReviewAttemptAt: this.lastReviewAttemptAt
|
|
12547
|
+
});
|
|
12548
|
+
}
|
|
12549
|
+
}
|
|
12550
|
+
this.mark("review");
|
|
12551
|
+
const result = await this.coordinator.review(this.agent, controller.signal, () => {
|
|
12552
|
+
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed(true)) controller.abort(/* @__PURE__ */ new Error("Automatic idle review admission changed"));
|
|
12553
|
+
controller.signal.throwIfAborted();
|
|
12554
|
+
});
|
|
12102
12555
|
if (controller.signal.aborted) return;
|
|
12103
12556
|
this.lastReviewAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12104
12557
|
this.lastReviewAction = result.action;
|
|
@@ -12119,6 +12572,7 @@ var MnemonAgentLifecycle = class {
|
|
|
12119
12572
|
}
|
|
12120
12573
|
}
|
|
12121
12574
|
cancelIdleReview(abortRunning) {
|
|
12575
|
+
this.reviewEpoch += 1;
|
|
12122
12576
|
if (this.idleReviewTimer !== void 0) clearTimeout(this.idleReviewTimer);
|
|
12123
12577
|
this.idleReviewTimer = void 0;
|
|
12124
12578
|
if (abortRunning) this.reviewController?.abort();
|
|
@@ -12282,11 +12736,15 @@ var MnemonLifecycle = class {
|
|
|
12282
12736
|
return this.skillEvolution.reviewNow(this.liveAgent(sessionId));
|
|
12283
12737
|
}
|
|
12284
12738
|
start() {
|
|
12739
|
+
const stopChanged = this.runtimeSource?.onChanged?.(() => {
|
|
12740
|
+
for (const { lifecycle } of this.owners.values()) lifecycle.configurationChanged();
|
|
12741
|
+
});
|
|
12285
12742
|
const stopCreated = this.ctx.on("agent/created", (({ agent, source }) => {
|
|
12286
12743
|
this.install(agent, source);
|
|
12287
12744
|
}));
|
|
12288
12745
|
for (const agent of this.ctx.agents.roots()) this.install(agent, "adopted");
|
|
12289
12746
|
return () => {
|
|
12747
|
+
stopChanged?.();
|
|
12290
12748
|
stopCreated();
|
|
12291
12749
|
for (const dispose of [...this.children.values()].reverse()) dispose();
|
|
12292
12750
|
for (const owner of [...this.owners.values()].reverse()) owner.dispose();
|
|
@@ -12791,7 +13249,7 @@ var MnemonLifecycle = class {
|
|
|
12791
13249
|
}
|
|
12792
13250
|
if (!this.ctx.agents.roots().includes(agent)) return;
|
|
12793
13251
|
const memoryTurn = this.runtimeSource === void 0 ? void 0 : new AgentMemoryTurn(agent, this.runtimeSource);
|
|
12794
|
-
const lifecycle = new MnemonAgentLifecycle(agent, this.coordinator, this.config, this.counters, source, memoryTurn);
|
|
13252
|
+
const lifecycle = new MnemonAgentLifecycle(agent, this.coordinator, this.config, this.counters, source, memoryTurn, () => this.runtimeSource?.forAgent(agent).directory);
|
|
12795
13253
|
let dispose;
|
|
12796
13254
|
dispose = agent.ctx.effect(() => {
|
|
12797
13255
|
const stop = lifecycle.start();
|
|
@@ -15333,16 +15791,59 @@ function documentActionReceipt(receipt) {
|
|
|
15333
15791
|
}
|
|
15334
15792
|
/**
|
|
15335
15793
|
* Named product tools are views over the same Source Route/Action protocol. Each family registers its tools in the
|
|
15336
|
-
*
|
|
15337
|
-
*
|
|
15794
|
+
* historical registry order, which puts Memory Spaces' reads before the Documents tools and its writes after
|
|
15795
|
+
* the runtime memory tool. DSH's prompt assembler independently orders wire schemas.
|
|
15338
15796
|
*/
|
|
15339
|
-
function registerTools(ctx, runtimeSource, coordinator) {
|
|
15797
|
+
function registerTools(ctx, runtimeSource, coordinator, contributions, initialReady) {
|
|
15798
|
+
const definitions = [];
|
|
15799
|
+
const catalog = { tools: { register: (tool) => {
|
|
15800
|
+
definitions.push(tool);
|
|
15801
|
+
} } };
|
|
15340
15802
|
const helpers = toolHelpers(runtimeSource);
|
|
15341
|
-
registerViewTools(
|
|
15342
|
-
registerMemorySpaceReadTools(
|
|
15343
|
-
registerDocumentTools(
|
|
15344
|
-
registerRuntimeTools(
|
|
15345
|
-
registerMemorySpaceWriteTools(
|
|
15803
|
+
registerViewTools(catalog, helpers, coordinator);
|
|
15804
|
+
registerMemorySpaceReadTools(catalog, helpers, coordinator);
|
|
15805
|
+
registerDocumentTools(catalog, helpers, coordinator);
|
|
15806
|
+
registerRuntimeTools(catalog, helpers, coordinator);
|
|
15807
|
+
registerMemorySpaceWriteTools(catalog, helpers, coordinator);
|
|
15808
|
+
const registered = /* @__PURE__ */ new Map();
|
|
15809
|
+
let active = true;
|
|
15810
|
+
let ready = initialReady === void 0;
|
|
15811
|
+
const reconcile = (snapshot) => {
|
|
15812
|
+
const types = new Set(snapshot.sources.map((source) => source.definition.manifest.typeId));
|
|
15813
|
+
for (const tool of definitions) {
|
|
15814
|
+
const type = memoryTool(tool.name)?.sourceTypeId;
|
|
15815
|
+
if (!(type === void 0 || ready && types.has(type))) {
|
|
15816
|
+
registered.get(tool.name)?.();
|
|
15817
|
+
registered.delete(tool.name);
|
|
15818
|
+
} else if (!registered.has(tool.name)) {
|
|
15819
|
+
const dispose = ctx.tools.register(tool);
|
|
15820
|
+
if (typeof dispose !== "function") throw new Error("dsh-mnemon tool registration did not return a disposer: " + tool.name);
|
|
15821
|
+
registered.set(tool.name, dispose);
|
|
15822
|
+
}
|
|
15823
|
+
}
|
|
15824
|
+
};
|
|
15825
|
+
const unsubscribe = contributions.onCommittedContributionsChanged(reconcile);
|
|
15826
|
+
const dispose = () => {
|
|
15827
|
+
if (!active) return;
|
|
15828
|
+
active = false;
|
|
15829
|
+
unsubscribe();
|
|
15830
|
+
for (const release of registered.values()) release();
|
|
15831
|
+
registered.clear();
|
|
15832
|
+
};
|
|
15833
|
+
try {
|
|
15834
|
+
reconcile(contributions.committedContributionSnapshot());
|
|
15835
|
+
} catch (error) {
|
|
15836
|
+
dispose();
|
|
15837
|
+
throw error;
|
|
15838
|
+
}
|
|
15839
|
+
initialReady?.then(() => {
|
|
15840
|
+
if (!active) return;
|
|
15841
|
+
ready = true;
|
|
15842
|
+
reconcile(contributions.committedContributionSnapshot());
|
|
15843
|
+
}).catch((error) => {
|
|
15844
|
+
if (active) console.warn("dsh-mnemon: could not initialize Source tool families", error);
|
|
15845
|
+
});
|
|
15846
|
+
return dispose;
|
|
15346
15847
|
}
|
|
15347
15848
|
/** What the handlers of every family share: the calling Agent's runtime graph and Sources, its checks, and a Source Action call. */
|
|
15348
15849
|
function toolHelpers(runtimeSource) {
|
|
@@ -18274,7 +18775,10 @@ function apply(rawContext, rawConfig = {}) {
|
|
|
18274
18775
|
runtime.dispose();
|
|
18275
18776
|
};
|
|
18276
18777
|
}, "dsh-mnemon.lifecycle-root()");
|
|
18277
|
-
|
|
18778
|
+
ctx.effect(() => {
|
|
18779
|
+
const loader = ctx.get("loader");
|
|
18780
|
+
return registerTools(ctx, runtime, coordinator, extensions, loader?.await?.());
|
|
18781
|
+
}, "dsh-mnemon: Source tool lifecycle");
|
|
18278
18782
|
registerCommands(ctx.commands, runtime, coordinator);
|
|
18279
18783
|
registerGuidance(ctx, resolved);
|
|
18280
18784
|
ctx.inject(["connection", "webServer"], (webContext) => {
|