dsh-mnemon 0.5.37 → 0.5.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/repair-legacy-session.mjs +86 -10
- package/lib/bundle.js +1 -1
- package/lib/client.js +485 -116
- package/lib/core.js +1 -1
- package/lib/index.js +1177 -551
- package/lib/{runtime-zuuBe8x1.js → runtime-0u6dOiF9.js} +33 -1
- package/lib/{starter-resolution-DYeYvfwr.js → starter-resolution-C3yJawlA.js} +5 -5
- package/lib/starter.js +1 -1
- package/lib/testing.js +1 -1
- package/lib/types/src/client/locales.d.ts +15 -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 +13 -2
- package/package.json +39 -39
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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-
|
|
1
|
+
import { d as truncate, 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-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";
|
|
@@ -259,7 +259,7 @@ const MNEMON_SYNC_CHANNEL = "/dsh-mnemon-sync";
|
|
|
259
259
|
const MNEMON_SYNC_TOKEN_ENV = "MNEMON_SYNC_GIT_TOKEN";
|
|
260
260
|
/**
|
|
261
261
|
* What a storage root publishes before anyone configures it: the branch, the
|
|
262
|
-
* directory inside it, and the
|
|
262
|
+
* directory inside it, and the fallback when no author or Git identity is set. They
|
|
263
263
|
* are declared with the wire constants because the settings form shows the same
|
|
264
264
|
* defaults the Host would apply to an empty field.
|
|
265
265
|
*/
|
|
@@ -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",
|
|
@@ -386,6 +387,7 @@ const DEFAULT_SKILLS_CONFIG = Object.freeze({
|
|
|
386
387
|
evolution: false,
|
|
387
388
|
allowCreate: true,
|
|
388
389
|
allowUpdate: true,
|
|
390
|
+
targetScope: "user",
|
|
389
391
|
autoBacktest: true,
|
|
390
392
|
hints: true,
|
|
391
393
|
sidebar: false,
|
|
@@ -473,6 +475,11 @@ const SkillsConfig = z.object({
|
|
|
473
475
|
evolution: z.boolean().default(DEFAULT_SKILLS_CONFIG.evolution),
|
|
474
476
|
allowCreate: z.boolean().default(DEFAULT_SKILLS_CONFIG.allowCreate),
|
|
475
477
|
allowUpdate: z.boolean().default(DEFAULT_SKILLS_CONFIG.allowUpdate),
|
|
478
|
+
targetScope: z.union([
|
|
479
|
+
"storage",
|
|
480
|
+
"user",
|
|
481
|
+
"project"
|
|
482
|
+
]).default(DEFAULT_SKILLS_CONFIG.targetScope),
|
|
476
483
|
autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
|
|
477
484
|
hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
|
|
478
485
|
sidebar: z.boolean().default(DEFAULT_SKILLS_CONFIG.sidebar),
|
|
@@ -535,6 +542,7 @@ const RuntimeMemorySchema = z.object({
|
|
|
535
542
|
const IdleReviewSchema = z.object({
|
|
536
543
|
enabled: z.boolean().default(DEFAULT_IDLE_REVIEW.enabled),
|
|
537
544
|
runtimeMemory: z.boolean().default(DEFAULT_IDLE_REVIEW.runtimeMemory),
|
|
545
|
+
persistWatermark: z.boolean().default(DEFAULT_IDLE_REVIEW.persistWatermark),
|
|
538
546
|
provider: z.union(["spawn", "fork"]).default(DEFAULT_IDLE_REVIEW.provider),
|
|
539
547
|
fallback: z.union(["spawn", "skip"]).default(DEFAULT_IDLE_REVIEW.fallback),
|
|
540
548
|
agentTeams: z.union(["pause", "scoped"]).default(DEFAULT_IDLE_REVIEW.agentTeams),
|
|
@@ -1257,7 +1265,7 @@ const LOADED_VERSION = manifest$1(PACKAGE_MANIFEST_PATH)?.version;
|
|
|
1257
1265
|
function pluginPackageVersion() {
|
|
1258
1266
|
return LOADED_VERSION ?? "0.0.0";
|
|
1259
1267
|
}
|
|
1260
|
-
const CHECK_TIMEOUT_MS = 1e4;
|
|
1268
|
+
const CHECK_TIMEOUT_MS$1 = 1e4;
|
|
1261
1269
|
const UPDATE_TIMEOUT_MS = 6e5;
|
|
1262
1270
|
/** A check waits this long for an update that is finishing, within the browser's 15 s check deadline. */
|
|
1263
1271
|
const IN_FLIGHT_WAIT_MS = 3e3;
|
|
@@ -1353,7 +1361,7 @@ async function fetchJson(url) {
|
|
|
1353
1361
|
const controller = new AbortController();
|
|
1354
1362
|
const timeout = setTimeout(() => {
|
|
1355
1363
|
controller.abort();
|
|
1356
|
-
}, CHECK_TIMEOUT_MS);
|
|
1364
|
+
}, CHECK_TIMEOUT_MS$1);
|
|
1357
1365
|
try {
|
|
1358
1366
|
const response = await fetch(url, {
|
|
1359
1367
|
signal: controller.signal,
|
|
@@ -1621,7 +1629,7 @@ var VersionUpdateManager = class {
|
|
|
1621
1629
|
let current;
|
|
1622
1630
|
try {
|
|
1623
1631
|
const invocation = resolveMnemonInvocation(command, ["--version"]);
|
|
1624
|
-
current = versionFrom((await resultOrThrow(this.processRunner, invocation.command, invocation.args, CHECK_TIMEOUT_MS, { env: invocation.env })).stdout);
|
|
1632
|
+
current = versionFrom((await resultOrThrow(this.processRunner, invocation.command, invocation.args, CHECK_TIMEOUT_MS$1, { env: invocation.env })).stdout);
|
|
1625
1633
|
} catch {
|
|
1626
1634
|
return { install: {
|
|
1627
1635
|
mode: launcher === void 0 ? "manual" : "npm",
|
|
@@ -1642,7 +1650,7 @@ var VersionUpdateManager = class {
|
|
|
1642
1650
|
...npm.args,
|
|
1643
1651
|
"root",
|
|
1644
1652
|
"--global"
|
|
1645
|
-
], CHECK_TIMEOUT_MS, { env: npm.env })).stdout.trim();
|
|
1653
|
+
], CHECK_TIMEOUT_MS$1, { env: npm.env })).stdout.trim();
|
|
1646
1654
|
if (isAbsolute(globalRoot) && samePath(dirname(dirname(launcher)), join(globalRoot, MNEMON_NPM_PACKAGE))) {
|
|
1647
1655
|
install.hint = "npm";
|
|
1648
1656
|
install.updateCommand = process.execPath;
|
|
@@ -1703,7 +1711,7 @@ var VersionUpdateManager = class {
|
|
|
1703
1711
|
"GOARCH",
|
|
1704
1712
|
"GOHOSTOS",
|
|
1705
1713
|
"GOHOSTARCH"
|
|
1706
|
-
], CHECK_TIMEOUT_MS)).stdout);
|
|
1714
|
+
], CHECK_TIMEOUT_MS$1)).stdout);
|
|
1707
1715
|
const goPath = typeof environment.GOPATH === "string" ? environment.GOPATH.split(delimiter)[0] : void 0;
|
|
1708
1716
|
const bin = typeof environment.GOBIN === "string" && environment.GOBIN !== "" ? environment.GOBIN : goPath === void 0 || goPath === "" ? void 0 : join(goPath, "bin");
|
|
1709
1717
|
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 +1725,7 @@ var VersionUpdateManager = class {
|
|
|
1717
1725
|
"version",
|
|
1718
1726
|
"-m",
|
|
1719
1727
|
command
|
|
1720
|
-
], CHECK_TIMEOUT_MS)).stdout.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((fields) => fields[0] === "path")?.[1] === MNEMON_MODULE) return {
|
|
1728
|
+
], CHECK_TIMEOUT_MS$1)).stdout.split(/\r?\n/).map((line) => line.trim().split(/\s+/)).find((fields) => fields[0] === "path")?.[1] === MNEMON_MODULE) return {
|
|
1721
1729
|
...current === void 0 ? {} : { current },
|
|
1722
1730
|
install: {
|
|
1723
1731
|
mode: "go",
|
|
@@ -3994,7 +4002,7 @@ function shapeOf(answer) {
|
|
|
3994
4002
|
const listed = keys.length > names.length ? [...names, String(keys.length - names.length) + " other"] : names;
|
|
3995
4003
|
return "HTTP " + String(answer.status) + ", content-type " + (answer.type ?? "none") + ", fields: " + (listed.length === 0 ? "none" : listed.join(", "));
|
|
3996
4004
|
}
|
|
3997
|
-
function failure$
|
|
4005
|
+
function failure$3(action, status, body) {
|
|
3998
4006
|
const code = codeOf(body);
|
|
3999
4007
|
const message = code === void 0 ? action + " failed with HTTP " + String(status) : action + " failed: " + code;
|
|
4000
4008
|
return status === 401 ? new MnemonSyncError("github-unauthorized", message) : new Error(message);
|
|
@@ -4135,7 +4143,7 @@ var MnemonGitHubAuth = class {
|
|
|
4135
4143
|
} catch (error) {
|
|
4136
4144
|
throw new Error("the GitHub device code request could not reach GitHub: " + describeTransportError(error));
|
|
4137
4145
|
}
|
|
4138
|
-
if (answer.status !== 200) throw failure$
|
|
4146
|
+
if (answer.status !== 200) throw failure$3("the GitHub device code request", answer.status, answer.body);
|
|
4139
4147
|
const fields = record$6(answer.body);
|
|
4140
4148
|
const deviceCode = text$1(fields?.device_code);
|
|
4141
4149
|
const userCode = text$1(fields?.user_code);
|
|
@@ -4283,7 +4291,7 @@ var MnemonGitHubAuth = class {
|
|
|
4283
4291
|
headers: this.authorization(token),
|
|
4284
4292
|
...signal === void 0 ? {} : { signal }
|
|
4285
4293
|
}, "listing GitHub repositories");
|
|
4286
|
-
if (status !== 200) throw failure$
|
|
4294
|
+
if (status !== 200) throw failure$3("listing GitHub repositories", status, body);
|
|
4287
4295
|
if (!Array.isArray(body)) throw new Error("the GitHub repository answer was not a list");
|
|
4288
4296
|
for (const entry of body) {
|
|
4289
4297
|
const repository = repositoryOf(entry);
|
|
@@ -4314,7 +4322,7 @@ var MnemonGitHubAuth = class {
|
|
|
4314
4322
|
}),
|
|
4315
4323
|
...signal === void 0 ? {} : { signal }
|
|
4316
4324
|
}, "creating the GitHub repository");
|
|
4317
|
-
if (status !== 201) throw failure$
|
|
4325
|
+
if (status !== 201) throw failure$3("creating the GitHub repository", status, body);
|
|
4318
4326
|
const repository = repositoryOf(body);
|
|
4319
4327
|
if (repository === void 0) throw new Error("the created GitHub repository could not be read back");
|
|
4320
4328
|
return repository;
|
|
@@ -5329,7 +5337,7 @@ var MnemonGitSync = class {
|
|
|
5329
5337
|
if (committed) {
|
|
5330
5338
|
const identity = await this.commitIdentity(settings, signal);
|
|
5331
5339
|
const result = await this.git([
|
|
5332
|
-
...identity,
|
|
5340
|
+
...identity.args,
|
|
5333
5341
|
"commit",
|
|
5334
5342
|
"--quiet",
|
|
5335
5343
|
"-m",
|
|
@@ -5338,7 +5346,8 @@ var MnemonGitSync = class {
|
|
|
5338
5346
|
settings.subdir
|
|
5339
5347
|
], {
|
|
5340
5348
|
cwd: this.store.mirror(),
|
|
5341
|
-
signal
|
|
5349
|
+
signal,
|
|
5350
|
+
env: identity.env
|
|
5342
5351
|
});
|
|
5343
5352
|
if (result.exitCode !== 0) throw new Error("git commit failed: " + tail(result.stderr || result.stdout));
|
|
5344
5353
|
}
|
|
@@ -5371,20 +5380,35 @@ var MnemonGitSync = class {
|
|
|
5371
5380
|
};
|
|
5372
5381
|
}
|
|
5373
5382
|
/**
|
|
5374
|
-
* The
|
|
5375
|
-
*
|
|
5376
|
-
*
|
|
5377
|
-
*
|
|
5383
|
+
* The saved default pair also appears in older settings that never selected an
|
|
5384
|
+
* author. Like blank fields, it defers to Git in the mirror's directory; only a
|
|
5385
|
+
* machine without a configured identity uses the channel's placeholder. Any
|
|
5386
|
+
* other pair keeps its explicit fields, even when one matches a default value.
|
|
5378
5387
|
*/
|
|
5379
5388
|
async commitIdentity(settings, signal) {
|
|
5380
|
-
const
|
|
5381
|
-
const
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5389
|
+
const defaults = settings.authorName === "dsh-mnemon sync" && settings.authorEmail === "mnemon@localhost";
|
|
5390
|
+
const authorName = defaults ? "" : settings.authorName;
|
|
5391
|
+
const authorEmail = defaults ? "" : settings.authorEmail;
|
|
5392
|
+
const name = authorName !== "" ? authorName : await this.configured("user.name", signal) ?? "dsh-mnemon sync";
|
|
5393
|
+
const email = authorEmail !== "" ? authorEmail : await this.configured("user.email", signal) ?? "mnemon@localhost";
|
|
5394
|
+
const env = {};
|
|
5395
|
+
if (authorName !== "") {
|
|
5396
|
+
env.GIT_AUTHOR_NAME = authorName;
|
|
5397
|
+
env.GIT_COMMITTER_NAME = authorName;
|
|
5398
|
+
}
|
|
5399
|
+
if (authorEmail !== "") {
|
|
5400
|
+
env.GIT_AUTHOR_EMAIL = authorEmail;
|
|
5401
|
+
env.GIT_COMMITTER_EMAIL = authorEmail;
|
|
5402
|
+
}
|
|
5403
|
+
return {
|
|
5404
|
+
args: [
|
|
5405
|
+
"-c",
|
|
5406
|
+
"user.name=" + name,
|
|
5407
|
+
"-c",
|
|
5408
|
+
"user.email=" + email
|
|
5409
|
+
],
|
|
5410
|
+
env
|
|
5411
|
+
};
|
|
5388
5412
|
}
|
|
5389
5413
|
/** One Git setting as this machine resolves it, or nothing when no config sets it. */
|
|
5390
5414
|
async configured(key, signal) {
|
|
@@ -6115,7 +6139,7 @@ var MnemonGitSync = class {
|
|
|
6115
6139
|
const failure = rejectedPush(output) ? "push-rejected" : REMOTE_FAILURE[remoteIssueOf(output)];
|
|
6116
6140
|
return {
|
|
6117
6141
|
pushed: false,
|
|
6118
|
-
reason: credential.token === void 0 && failure !== "push-rejected" ? "the commit stays local because no token is available: " + reason : reason,
|
|
6142
|
+
reason: /\[remote rejected\]/iu.test(output) ? "the remote rejected this push; check its policy and the commit author in Sync > Advanced, then sync again: " + reason : credential.token === void 0 && failure !== "push-rejected" ? "the commit stays local because no token is available: " + reason : reason,
|
|
6119
6143
|
...failure === void 0 ? {} : { failure }
|
|
6120
6144
|
};
|
|
6121
6145
|
}
|
|
@@ -6277,7 +6301,10 @@ var MnemonGitSync = class {
|
|
|
6277
6301
|
try {
|
|
6278
6302
|
return await this.run(GIT_LABEL, prepared.args, {
|
|
6279
6303
|
timeoutMs: options.timeoutMs ?? 12e4,
|
|
6280
|
-
env: gitEnvironment(
|
|
6304
|
+
env: gitEnvironment({
|
|
6305
|
+
...process.env,
|
|
6306
|
+
...options.env
|
|
6307
|
+
}),
|
|
6281
6308
|
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
6282
6309
|
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
6283
6310
|
...options.maxOutputBytes === void 0 ? {} : { maxOutputBytes: options.maxOutputBytes },
|
|
@@ -7490,6 +7517,7 @@ var LiveMnemonRuntime = class {
|
|
|
7490
7517
|
githubAuth;
|
|
7491
7518
|
autoBackupView;
|
|
7492
7519
|
closed = false;
|
|
7520
|
+
changeListeners = /* @__PURE__ */ new Set();
|
|
7493
7521
|
config;
|
|
7494
7522
|
storage;
|
|
7495
7523
|
packs;
|
|
@@ -7531,6 +7559,12 @@ var LiveMnemonRuntime = class {
|
|
|
7531
7559
|
applyAutoBackup(graph) {
|
|
7532
7560
|
if (this.autoBackupView !== void 0) graph.sync.useAutoBackup(this.autoBackupView);
|
|
7533
7561
|
}
|
|
7562
|
+
onChanged(listener) {
|
|
7563
|
+
this.changeListeners.add(listener);
|
|
7564
|
+
return () => {
|
|
7565
|
+
this.changeListeners.delete(listener);
|
|
7566
|
+
};
|
|
7567
|
+
}
|
|
7534
7568
|
swap(next) {
|
|
7535
7569
|
if (this.closed) {
|
|
7536
7570
|
next.dispose();
|
|
@@ -7544,6 +7578,7 @@ var LiveMnemonRuntime = class {
|
|
|
7544
7578
|
this.retireGraph(previous);
|
|
7545
7579
|
for (const graph of this.workspaceGraphs.values()) this.retireGraph(graph);
|
|
7546
7580
|
this.workspaceGraphs.clear();
|
|
7581
|
+
for (const listener of this.changeListeners) listener();
|
|
7547
7582
|
}
|
|
7548
7583
|
snapshot() {
|
|
7549
7584
|
this.assertOpen();
|
|
@@ -7568,6 +7603,7 @@ var LiveMnemonRuntime = class {
|
|
|
7568
7603
|
dispose() {
|
|
7569
7604
|
if (this.closed) return;
|
|
7570
7605
|
this.executions.dispose();
|
|
7606
|
+
this.changeListeners.clear();
|
|
7571
7607
|
this.closed = true;
|
|
7572
7608
|
const graphs = /* @__PURE__ */ new Set([
|
|
7573
7609
|
this.current,
|
|
@@ -10158,148 +10194,535 @@ async function assertAutomaticMemoryWrite(host, parent) {
|
|
|
10158
10194
|
return host.writableSourceFor(parent, "memory-spaces", "remember");
|
|
10159
10195
|
}
|
|
10160
10196
|
//#endregion
|
|
10161
|
-
//#region src/host/
|
|
10162
|
-
function evidenceInsights(evidence) {
|
|
10163
|
-
return evidence.items.map((item) => {
|
|
10164
|
-
return {
|
|
10165
|
-
...optionalObject(item.provenance) ?? {},
|
|
10166
|
-
id: item.id,
|
|
10167
|
-
content: item.text,
|
|
10168
|
-
...item.score === void 0 ? {} : { score: item.score },
|
|
10169
|
-
...item.revision === void 0 ? {} : { revision: item.revision }
|
|
10170
|
-
};
|
|
10171
|
-
});
|
|
10172
|
-
}
|
|
10197
|
+
//#region src/host/view-presentation.ts
|
|
10173
10198
|
/**
|
|
10174
|
-
*
|
|
10175
|
-
*
|
|
10176
|
-
*
|
|
10177
|
-
* rely on this line, documented as stable, to leave Mnemon's own children out.
|
|
10199
|
+
* The named tools bound to each Source type's Routes or Actions, from the tool table: these are
|
|
10200
|
+
* existing DSH product-tool bindings, not Core operation names. Tools that share one operation are
|
|
10201
|
+
* listed together, in table order, as the View's tool list names them.
|
|
10178
10202
|
*/
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
const
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
"mnemon_recall",
|
|
10187
|
-
"mnemon_related"
|
|
10188
|
-
],
|
|
10189
|
-
"mnemon_remember",
|
|
10190
|
-
"mnemon_link",
|
|
10191
|
-
"mnemon_forget",
|
|
10192
|
-
"mnemon_memory_body_create",
|
|
10193
|
-
"mnemon_memory_body_update",
|
|
10194
|
-
"mnemon_memory_body_merge"
|
|
10195
|
-
];
|
|
10196
|
-
const AUTONOMOUS_WRITE_TOOLS = WRITE_TOOLS.filter((tool) => tool !== "mnemon_forget");
|
|
10197
|
-
const EXPLICIT_WRITE_TOOLS = WRITE_TOOLS;
|
|
10198
|
-
const REVIEW_TOOLS = [
|
|
10199
|
-
...["mnemon_document_search"],
|
|
10200
|
-
"mnemon_runtime_memory",
|
|
10201
|
-
"mnemon_document_create"
|
|
10202
|
-
];
|
|
10203
|
-
const RESULT_TOOL_NAME = "mnemon_subagent_result";
|
|
10204
|
-
/** How many finished delegated children stay refused memory tools if DSH resumes one. */
|
|
10205
|
-
const FINISHED_CHILDREN = 1e3;
|
|
10206
|
-
const FINISHED_CHILD_REFUSAL = "This Mnemon task has finished, so it no longer reads or changes memory. Reply without memory tools.";
|
|
10207
|
-
const RESULT_TOOL_INPUT_SCHEMA = {
|
|
10208
|
-
type: "object",
|
|
10209
|
-
properties: {
|
|
10210
|
-
requestId: {
|
|
10211
|
-
type: "string",
|
|
10212
|
-
minLength: 1
|
|
10213
|
-
},
|
|
10214
|
-
result: {
|
|
10215
|
-
type: "object",
|
|
10216
|
-
additionalProperties: true
|
|
10217
|
-
}
|
|
10218
|
-
},
|
|
10219
|
-
required: ["requestId", "result"],
|
|
10220
|
-
additionalProperties: false
|
|
10221
|
-
};
|
|
10222
|
-
const RESULT_TOOL_OUTPUT_SCHEMA = {
|
|
10223
|
-
type: "object",
|
|
10224
|
-
properties: { recorded: {
|
|
10225
|
-
type: "boolean",
|
|
10226
|
-
const: true
|
|
10227
|
-
} },
|
|
10228
|
-
required: ["recorded"],
|
|
10229
|
-
additionalProperties: false
|
|
10230
|
-
};
|
|
10231
|
-
const WRITE_OPERATION_RESULT_TOOL = {
|
|
10232
|
-
remember: "mnemon_remember",
|
|
10233
|
-
"supervised-writeback": "mnemon_remember",
|
|
10234
|
-
link: "mnemon_link",
|
|
10235
|
-
forget: "mnemon_forget",
|
|
10236
|
-
"create-memory-body": "mnemon_memory_body_create",
|
|
10237
|
-
"update-memory-body": "mnemon_memory_body_update",
|
|
10238
|
-
"merge-memory-bodies": "mnemon_memory_body_merge"
|
|
10239
|
-
};
|
|
10240
|
-
var IdleReviewError = class extends Error {
|
|
10241
|
-
review;
|
|
10242
|
-
constructor(message, review, options) {
|
|
10243
|
-
super(message, options);
|
|
10244
|
-
this.review = review;
|
|
10245
|
-
this.name = "IdleReviewError";
|
|
10203
|
+
function namedBindings(kind) {
|
|
10204
|
+
const bindings = {};
|
|
10205
|
+
for (const [name, tool] of memoryTools()) {
|
|
10206
|
+
if (tool.sourceTypeId === void 0 || tool.operation === void 0 || tool.kind === "write" !== (kind === "action")) continue;
|
|
10207
|
+
const operations = bindings[tool.sourceTypeId] ??= {};
|
|
10208
|
+
const listed = operations[tool.operation];
|
|
10209
|
+
operations[tool.operation] = listed === void 0 ? name : `${listed} / ${name}`;
|
|
10246
10210
|
}
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
const
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10211
|
+
return bindings;
|
|
10212
|
+
}
|
|
10213
|
+
const ROUTE_TOOLS = namedBindings("route");
|
|
10214
|
+
const ACTION_TOOLS = namedBindings("action");
|
|
10215
|
+
const NAMED_SOURCE_TYPES = [.../* @__PURE__ */ new Set([...Object.keys(ROUTE_TOOLS), ...Object.keys(ACTION_TOOLS)])];
|
|
10216
|
+
/** Use a short availability list only where the Host has the matching named binding.
|
|
10217
|
+
* Unbound external operations retain exact ids and schemas in the generic envelope. */
|
|
10218
|
+
function modelMemoryWake(graph, turn) {
|
|
10219
|
+
const generation = graph.memoryComposition.generation(turn.view.runtimeGeneration);
|
|
10220
|
+
if (generation === void 0) throw new Error("Cannot present an unpinned Memory generation");
|
|
10221
|
+
return graph.composableTurns.memoryWake(turn.view.id, memoryWakeBindings(generation, turn.view));
|
|
10222
|
+
}
|
|
10223
|
+
function presentMemoryWake(generation, view) {
|
|
10224
|
+
return createMemoryWake(view, memoryWakeBindings(generation, view));
|
|
10260
10225
|
}
|
|
10261
|
-
/** A Memory Space write tool: each switches on the space it goes to. */
|
|
10262
|
-
const SPACE_WRITE_TOOLS = /* @__PURE__ */ new Set(["mnemon_remember", "mnemon_link"]);
|
|
10263
10226
|
/**
|
|
10264
|
-
*
|
|
10265
|
-
*
|
|
10266
|
-
*
|
|
10267
|
-
* named it (#387). A space the writer creates is not in `inactive` and takes its first
|
|
10268
|
-
* write. Saving a memory never merges spaces: a merge switches spaces on and off, and
|
|
10269
|
-
* is a request of its own.
|
|
10227
|
+
* Why a generic View tool names no id this turn's View offers, told apart (#390); undefined when
|
|
10228
|
+
* the id is offered. It names only what the MNEMON VIEW ROUTES envelope shows: an operation bound
|
|
10229
|
+
* to a named tool is offered through that tool, and the envelope does not list its id.
|
|
10270
10230
|
*/
|
|
10271
|
-
function
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10231
|
+
function unofferedViewId(graph, turn, kind, id) {
|
|
10232
|
+
const view = turn.view;
|
|
10233
|
+
const offered = kind === "route" ? view.routes.map((route) => ({
|
|
10234
|
+
id: route.id,
|
|
10235
|
+
operation: route.sourceRouteId
|
|
10236
|
+
})) : view.actionOffers.map((offer) => ({
|
|
10237
|
+
id: offer.id,
|
|
10238
|
+
operation: offer.sourceActionId
|
|
10239
|
+
}));
|
|
10240
|
+
const label = kind === "route" ? "memory View Route" : "memory ActionOffer";
|
|
10241
|
+
if (typeof id !== "string" || id === "") return `${label} needs ${kind === "route" ? "routeId" : "offerId"}, the exact id of one it offers`;
|
|
10242
|
+
if (offered.some((entry) => entry.id === id)) return void 0;
|
|
10243
|
+
const generation = graph.memoryComposition.generation(view.runtimeGeneration);
|
|
10244
|
+
const bindings = generation === void 0 ? {} : memoryWakeBindings(generation, view);
|
|
10245
|
+
const named = (kind === "route" ? bindings.routes : bindings.actions) ?? {};
|
|
10246
|
+
const short = id.split("/").at(-1);
|
|
10247
|
+
const tools = [...new Set(offered.filter((entry) => named[entry.id] !== void 0 && (entry.operation === short || entry.id.split("/").at(-1) === short)).map((entry) => named[entry.id]))];
|
|
10248
|
+
if (tools.length > 0) return `${label} is unavailable: ${id}; this View offers it as ${tools.join(" and ")}, so call that tool`;
|
|
10249
|
+
const shown = offered.filter((entry) => named[entry.id] === void 0).map((entry) => entry.id);
|
|
10250
|
+
if (shown.length === 0) return `${label} is unavailable: ${id}; MNEMON VIEW ROUTES offers none by id this turn, only the named tools in MNEMON VIEW TOOLS`;
|
|
10251
|
+
const near = shown.filter((candidate) => candidate.split("/").at(-1) === short);
|
|
10252
|
+
if (near.length === 1) return `${label} is unavailable: ${id}; ids are exact as MNEMON VIEW ROUTES lists them: ${JSON.stringify(near[0])}`;
|
|
10253
|
+
const listed = [...near, ...shown.filter((candidate) => !near.includes(candidate))];
|
|
10254
|
+
return `${label} is unavailable: ${id}; MNEMON VIEW ROUTES offers ${listed.slice(0, 12).map((candidate) => JSON.stringify(candidate)).join(", ")}${listed.length > 12 ? ` and ${listed.length - 12} more` : ""}`;
|
|
10255
|
+
}
|
|
10256
|
+
function memoryWakeBindings(generation, view) {
|
|
10257
|
+
const instances = generation.sourceInstances();
|
|
10258
|
+
const routes = {}, actions = {};
|
|
10259
|
+
for (const typeId of NAMED_SOURCE_TYPES) {
|
|
10260
|
+
const selected = namedToolInstance(instances.filter((source) => source.sourceTypeId === typeId), typeId);
|
|
10261
|
+
if (selected === void 0) continue;
|
|
10262
|
+
for (const route of view.routes.filter((route) => route.sourceInstanceKey === selected.sourceInstanceKey)) {
|
|
10263
|
+
if (view.readGrants.find((grant) => grant.id === route.readGrantId)?.schema !== `dsh-mnemon.${typeId}/v1`) continue;
|
|
10264
|
+
const tool = ROUTE_TOOLS[typeId]?.[route.sourceRouteId];
|
|
10265
|
+
if (tool !== void 0) routes[route.id] = tool;
|
|
10280
10266
|
}
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10267
|
+
for (const action of view.actionOffers.filter((action) => action.sourceInstanceKey === selected.sourceInstanceKey)) {
|
|
10268
|
+
const tool = ACTION_TOOLS[typeId]?.[action.sourceActionId];
|
|
10269
|
+
if (tool !== void 0) actions[action.id] = tool;
|
|
10270
|
+
}
|
|
10271
|
+
}
|
|
10272
|
+
return {
|
|
10273
|
+
routes,
|
|
10274
|
+
actions
|
|
10284
10275
|
};
|
|
10285
10276
|
}
|
|
10286
|
-
/**
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10277
|
+
/** Explicitly pick display fields; never copy grants, authority, or opaque Source state. */
|
|
10278
|
+
function inspectMemoryView(generation, view, state, turn, memoryText) {
|
|
10279
|
+
return structuredClone({
|
|
10280
|
+
id: view.id,
|
|
10281
|
+
digest: view.digest,
|
|
10282
|
+
generationId: view.runtimeGeneration,
|
|
10283
|
+
createdAt: view.createdAt,
|
|
10284
|
+
state,
|
|
10285
|
+
...turn === void 0 ? {} : { turn },
|
|
10286
|
+
strategyTypeId: view.strategyTypeId,
|
|
10287
|
+
strategyInstanceKey: view.strategyInstanceKey,
|
|
10288
|
+
extensions: view.strategyExtensions ?? [],
|
|
10289
|
+
projection: view.projection.map(({ id, sourceInstanceKey, mode, text, revision }) => ({
|
|
10290
|
+
id,
|
|
10291
|
+
sourceInstanceKey,
|
|
10292
|
+
mode,
|
|
10293
|
+
text,
|
|
10294
|
+
revision
|
|
10295
|
+
})),
|
|
10296
|
+
sourcePresentations: view.sourcePresentations ?? [],
|
|
10297
|
+
routes: view.routes.map(({ id, sourceInstanceKey, sourceRouteId, description, maxCalls }) => ({
|
|
10298
|
+
id,
|
|
10299
|
+
sourceInstanceKey,
|
|
10300
|
+
operationId: sourceRouteId,
|
|
10301
|
+
description,
|
|
10302
|
+
maxCalls
|
|
10303
|
+
})),
|
|
10304
|
+
actions: view.actionOffers.map(({ id, sourceInstanceKey, sourceActionId, description }) => ({
|
|
10305
|
+
id,
|
|
10306
|
+
sourceInstanceKey,
|
|
10307
|
+
operationId: sourceActionId,
|
|
10308
|
+
description
|
|
10309
|
+
})),
|
|
10310
|
+
memoryText: memoryText ?? presentMemoryWake(generation, view).text,
|
|
10311
|
+
...view.guidance === void 0 ? {} : { guidance: view.guidance },
|
|
10312
|
+
diagnostics: (view.diagnostics ?? []).map((diagnostic) => diagnostic.message)
|
|
10313
|
+
});
|
|
10301
10314
|
}
|
|
10302
|
-
|
|
10315
|
+
//#endregion
|
|
10316
|
+
//#region src/host/agent-memory-turn.ts
|
|
10317
|
+
/** The durable log, not a parent session id, identifies the executing turn. */
|
|
10318
|
+
function openAgentTurn(agent) {
|
|
10319
|
+
let open;
|
|
10320
|
+
for (const event of agent.session.snapshotEvents()) {
|
|
10321
|
+
const turn = typeof event.data.turn === "number" ? event.data.turn : void 0;
|
|
10322
|
+
if (event.type === "turn/start" && turn !== void 0) open = turn;
|
|
10323
|
+
else if (event.type === "turn/end" && turn === open) open = void 0;
|
|
10324
|
+
}
|
|
10325
|
+
return open;
|
|
10326
|
+
}
|
|
10327
|
+
/**
|
|
10328
|
+
* One Agent owns each turn pin. A child additionally retains its delegation
|
|
10329
|
+
* across parent completion, collection, and runtime swaps until it is disposed.
|
|
10330
|
+
*/
|
|
10331
|
+
var AgentMemoryTurn = class {
|
|
10332
|
+
agent;
|
|
10333
|
+
runtime;
|
|
10334
|
+
delegation;
|
|
10335
|
+
pinned;
|
|
10336
|
+
/** A turn that runs without memory because no composition was serving when it began. */
|
|
10337
|
+
unavailable;
|
|
10338
|
+
warned;
|
|
10339
|
+
pending;
|
|
10340
|
+
generation = 0;
|
|
10341
|
+
closed = false;
|
|
10342
|
+
lastInspection;
|
|
10343
|
+
lastWorkspace;
|
|
10344
|
+
releaseDelegation;
|
|
10345
|
+
constructor(agent, runtime, delegation) {
|
|
10346
|
+
this.agent = agent;
|
|
10347
|
+
this.runtime = runtime;
|
|
10348
|
+
this.delegation = delegation;
|
|
10349
|
+
if (delegation === void 0) return;
|
|
10350
|
+
this.releaseDelegation = runtime.executions.retain(agent, delegation);
|
|
10351
|
+
}
|
|
10352
|
+
get current() {
|
|
10353
|
+
return this.pinned;
|
|
10354
|
+
}
|
|
10355
|
+
/** Why the current turn has no View, when memory could not be composed for it. */
|
|
10356
|
+
get unavailableReason() {
|
|
10357
|
+
return this.unavailable?.reason;
|
|
10358
|
+
}
|
|
10359
|
+
inspect(workspaceRoot) {
|
|
10360
|
+
if (this.lastInspection === void 0) return void 0;
|
|
10361
|
+
if (workspaceRoot !== void 0 && resolve(workspaceRoot) !== this.lastWorkspace) return void 0;
|
|
10362
|
+
return structuredClone({
|
|
10363
|
+
...this.lastInspection,
|
|
10364
|
+
state: this.pinned === void 0 ? "recent" : "active"
|
|
10365
|
+
});
|
|
10366
|
+
}
|
|
10367
|
+
clearInspection() {
|
|
10368
|
+
this.lastInspection = void 0;
|
|
10369
|
+
this.lastWorkspace = void 0;
|
|
10370
|
+
}
|
|
10371
|
+
/** Capture now; descendants must not resolve a later parent turn on demand. */
|
|
10372
|
+
delegate() {
|
|
10373
|
+
if (this.closed) throw new Error("memory Agent lifetime has ended");
|
|
10374
|
+
if (this.pinned !== void 0) return {
|
|
10375
|
+
graph: this.pinned.graph,
|
|
10376
|
+
scope: this.pinned.context.scope,
|
|
10377
|
+
viewId: this.pinned.context.view.id
|
|
10378
|
+
};
|
|
10379
|
+
if (this.delegation !== void 0) return this.delegation;
|
|
10380
|
+
const graph = this.runtime.forAgent(this.agent);
|
|
10381
|
+
return {
|
|
10382
|
+
graph,
|
|
10383
|
+
scope: agentScope(this.agent, graph.config)
|
|
10384
|
+
};
|
|
10385
|
+
}
|
|
10386
|
+
async begin(turn, signal) {
|
|
10387
|
+
if (this.closed) throw new Error("memory Agent lifetime has ended");
|
|
10388
|
+
signal?.throwIfAborted();
|
|
10389
|
+
if (this.pinned?.turn === turn || this.unavailable?.turn === turn) return;
|
|
10390
|
+
if (this.pending?.turn === turn) return this.pending.result;
|
|
10391
|
+
this.end();
|
|
10392
|
+
const generation = this.generation;
|
|
10393
|
+
const controller = new AbortController();
|
|
10394
|
+
const abort = () => controller.abort(signal.reason);
|
|
10395
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
10396
|
+
const result = this.pin(turn, generation, controller.signal).catch((error) => {
|
|
10397
|
+
if (!isMemoryCompositionUnavailable(error) || this.closed || this.generation !== generation) throw error;
|
|
10398
|
+
this.unavailable = {
|
|
10399
|
+
turn,
|
|
10400
|
+
reason: error.message
|
|
10401
|
+
};
|
|
10402
|
+
this.clearInspection();
|
|
10403
|
+
if (this.warned !== error.message) console.warn(`[dsh-mnemon] this turn runs without memory: ${error.message}`);
|
|
10404
|
+
this.warned = error.message;
|
|
10405
|
+
});
|
|
10406
|
+
this.pending = {
|
|
10407
|
+
turn,
|
|
10408
|
+
generation,
|
|
10409
|
+
controller,
|
|
10410
|
+
result
|
|
10411
|
+
};
|
|
10412
|
+
try {
|
|
10413
|
+
await result;
|
|
10414
|
+
} finally {
|
|
10415
|
+
signal?.removeEventListener("abort", abort);
|
|
10416
|
+
if (this.pending?.generation === generation) this.pending = void 0;
|
|
10417
|
+
}
|
|
10418
|
+
}
|
|
10419
|
+
end(turn) {
|
|
10420
|
+
if (turn !== void 0 && this.pinned?.turn !== turn && this.pending?.turn !== turn && this.unavailable?.turn !== turn) return;
|
|
10421
|
+
this.generation += 1;
|
|
10422
|
+
this.unavailable = void 0;
|
|
10423
|
+
this.pending?.controller.abort(/* @__PURE__ */ new Error("memory turn ended during View preparation"));
|
|
10424
|
+
this.pending = void 0;
|
|
10425
|
+
const pinned = this.pinned;
|
|
10426
|
+
this.pinned = void 0;
|
|
10427
|
+
pinned?.release();
|
|
10428
|
+
}
|
|
10429
|
+
dispose() {
|
|
10430
|
+
if (this.closed) return;
|
|
10431
|
+
this.closed = true;
|
|
10432
|
+
this.clearInspection();
|
|
10433
|
+
try {
|
|
10434
|
+
this.end();
|
|
10435
|
+
} finally {
|
|
10436
|
+
this.releaseDelegation?.();
|
|
10437
|
+
}
|
|
10438
|
+
}
|
|
10439
|
+
async pin(turn, generation, signal) {
|
|
10440
|
+
const execution = await this.runtime.executions.turn(this.agent, turn, signal, this.delegation);
|
|
10441
|
+
const { graph, context } = execution;
|
|
10442
|
+
try {
|
|
10443
|
+
if (this.closed || this.generation !== generation) throw new Error("memory turn ended during View preparation");
|
|
10444
|
+
signal?.throwIfAborted();
|
|
10445
|
+
const inspection = inspectMemoryView(graph.memoryComposition.generation(context.view.runtimeGeneration), context.view, "active", turn, modelMemoryWake(graph, context).text);
|
|
10446
|
+
this.pinned = {
|
|
10447
|
+
turn,
|
|
10448
|
+
graph,
|
|
10449
|
+
context,
|
|
10450
|
+
release: execution.release
|
|
10451
|
+
};
|
|
10452
|
+
this.warned = void 0;
|
|
10453
|
+
this.lastInspection = inspection;
|
|
10454
|
+
this.lastWorkspace = context.scope.workspaceId === void 0 ? void 0 : resolve(context.scope.workspaceId);
|
|
10455
|
+
} catch (error) {
|
|
10456
|
+
execution.release();
|
|
10457
|
+
throw error;
|
|
10458
|
+
}
|
|
10459
|
+
}
|
|
10460
|
+
};
|
|
10461
|
+
//#endregion
|
|
10462
|
+
//#region src/host/tool-execution.ts
|
|
10463
|
+
/** DSH 0.2.2 exposes executor registration and batch dispatch together. */
|
|
10464
|
+
function usesToolBatchExecution(tools) {
|
|
10465
|
+
return typeof tools.setExecutor === "function";
|
|
10466
|
+
}
|
|
10467
|
+
/** Preserve the Host's scoped executor, guards, policy and final result for one call. */
|
|
10468
|
+
async function executeHostTool(tools, input) {
|
|
10469
|
+
if (tools.execute === void 0) throw new Error("The DSH tool execution service is unavailable");
|
|
10470
|
+
if (!usesToolBatchExecution(tools)) return tools.execute(input);
|
|
10471
|
+
const results = (await tools.execute({
|
|
10472
|
+
calls: [{
|
|
10473
|
+
type: "tool-call",
|
|
10474
|
+
id: input.callId,
|
|
10475
|
+
name: input.name,
|
|
10476
|
+
arguments: JSON.stringify(input.arguments)
|
|
10477
|
+
}],
|
|
10478
|
+
agent: input.agent,
|
|
10479
|
+
signal: input.signal
|
|
10480
|
+
}))?.results;
|
|
10481
|
+
const result = results?.[0];
|
|
10482
|
+
if (!Array.isArray(results) || results.length !== 1 || result?.callId !== input.callId || typeof result.result?.isError !== "boolean") throw new Error("The DSH tool batch did not return the requested call outcome");
|
|
10483
|
+
return result.result;
|
|
10484
|
+
}
|
|
10485
|
+
//#endregion
|
|
10486
|
+
//#region src/host/review-admission.ts
|
|
10487
|
+
const RESULT_TOOL = "mnemon_subagent_result";
|
|
10488
|
+
const CHECK_TIMEOUT_MS = 5e3;
|
|
10489
|
+
/** An in-turn no-op challenge through the mandatory review completion channel.
|
|
10490
|
+
* Inventory alone cannot see execution-time guards. No errors are classified as
|
|
10491
|
+
* permission failures, and an unavailable checkpoint is never a permanent ban.
|
|
10492
|
+
*/
|
|
10493
|
+
var AutomaticReviewAdmission = class {
|
|
10494
|
+
challenges = /* @__PURE__ */ new Map();
|
|
10495
|
+
controllers = /* @__PURE__ */ new Set();
|
|
10496
|
+
draining = /* @__PURE__ */ new WeakSet();
|
|
10497
|
+
disposed = false;
|
|
10498
|
+
/** Only an unexpired, host-issued challenge for this exact Agent can no-op. */
|
|
10499
|
+
acknowledge(args, execution) {
|
|
10500
|
+
if (typeof args !== "object" || args === null || !("requestId" in args) || typeof args.requestId !== "string") return false;
|
|
10501
|
+
const challenge = this.challenges.get(args.requestId);
|
|
10502
|
+
if (challenge === void 0 || challenge.agent !== execution.agent || challenge.execution !== void 0) return false;
|
|
10503
|
+
execution.signal.throwIfAborted();
|
|
10504
|
+
challenge.execution = execution;
|
|
10505
|
+
return true;
|
|
10506
|
+
}
|
|
10507
|
+
dispose() {
|
|
10508
|
+
this.disposed = true;
|
|
10509
|
+
this.challenges.clear();
|
|
10510
|
+
for (const controller of this.controllers) controller.abort();
|
|
10511
|
+
}
|
|
10512
|
+
async check(agent, signal) {
|
|
10513
|
+
signal.throwIfAborted();
|
|
10514
|
+
const tools = agent.ctx.tools;
|
|
10515
|
+
if (this.disposed || tools?.execute === void 0 || this.draining.has(agent) || openAgentTurn(agent) === void 0) return false;
|
|
10516
|
+
const controller = new AbortController();
|
|
10517
|
+
this.controllers.add(controller);
|
|
10518
|
+
const combined = AbortSignal.any([signal, controller.signal]);
|
|
10519
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
10520
|
+
timeout.unref?.();
|
|
10521
|
+
const attempt = async (codeMode) => {
|
|
10522
|
+
combined.throwIfAborted();
|
|
10523
|
+
const requestId = randomUUID();
|
|
10524
|
+
const callId = randomUUID();
|
|
10525
|
+
const challenge = {
|
|
10526
|
+
agent,
|
|
10527
|
+
accepted: false
|
|
10528
|
+
};
|
|
10529
|
+
this.challenges.set(requestId, challenge);
|
|
10530
|
+
const revoke = () => this.challenges.delete(requestId);
|
|
10531
|
+
combined.addEventListener("abort", revoke, { once: true });
|
|
10532
|
+
const stops = [];
|
|
10533
|
+
let executing = false;
|
|
10534
|
+
const cleanup = () => {
|
|
10535
|
+
for (const stop of stops.reverse()) stop();
|
|
10536
|
+
this.draining.delete(agent);
|
|
10537
|
+
};
|
|
10538
|
+
try {
|
|
10539
|
+
stops.push(agent.ctx.on("tools/result", ((execution, result) => {
|
|
10540
|
+
if (execution === challenge.execution) challenge.accepted = result.isError === false;
|
|
10541
|
+
})));
|
|
10542
|
+
stops.push(agent.ctx.on("tools/pre-execute", (async (execution, next) => {
|
|
10543
|
+
const decision = await next();
|
|
10544
|
+
return execution.agent === agent && (execution.callId === callId || execution.rootCallId === callId) && decision.kind === "ask" ? { kind: "cancel" } : decision;
|
|
10545
|
+
}), { prepend: true }));
|
|
10546
|
+
const args = {
|
|
10547
|
+
requestId,
|
|
10548
|
+
result: {}
|
|
10549
|
+
};
|
|
10550
|
+
const pending = executeHostTool(tools, {
|
|
10551
|
+
callId,
|
|
10552
|
+
agent,
|
|
10553
|
+
signal: combined,
|
|
10554
|
+
name: codeMode ? "run_code" : RESULT_TOOL,
|
|
10555
|
+
arguments: codeMode ? {
|
|
10556
|
+
description: "Check automatic memory review availability",
|
|
10557
|
+
code: `await tools.mnemon_subagent_result(${JSON.stringify(args)})`
|
|
10558
|
+
} : args
|
|
10559
|
+
});
|
|
10560
|
+
executing = true;
|
|
10561
|
+
this.draining.add(agent);
|
|
10562
|
+
const outcome = await pending.finally(cleanup);
|
|
10563
|
+
combined.throwIfAborted();
|
|
10564
|
+
return challenge.execution !== void 0 && challenge.accepted && outcome.isError === false;
|
|
10565
|
+
} finally {
|
|
10566
|
+
this.challenges.delete(requestId);
|
|
10567
|
+
combined.removeEventListener("abort", revoke);
|
|
10568
|
+
if (!executing) cleanup();
|
|
10569
|
+
}
|
|
10570
|
+
};
|
|
10571
|
+
try {
|
|
10572
|
+
if (await attempt(false)) return true;
|
|
10573
|
+
return (usesToolBatchExecution(tools) || tools.get?.("run_code", agent) !== void 0) && await attempt(true);
|
|
10574
|
+
} catch {
|
|
10575
|
+
signal.throwIfAborted();
|
|
10576
|
+
return false;
|
|
10577
|
+
} finally {
|
|
10578
|
+
clearTimeout(timeout);
|
|
10579
|
+
this.controllers.delete(controller);
|
|
10580
|
+
}
|
|
10581
|
+
}
|
|
10582
|
+
};
|
|
10583
|
+
//#endregion
|
|
10584
|
+
//#region src/host/subagent.ts
|
|
10585
|
+
function evidenceInsights(evidence) {
|
|
10586
|
+
return evidence.items.map((item) => {
|
|
10587
|
+
return {
|
|
10588
|
+
...optionalObject(item.provenance) ?? {},
|
|
10589
|
+
id: item.id,
|
|
10590
|
+
content: item.text,
|
|
10591
|
+
...item.score === void 0 ? {} : { score: item.score },
|
|
10592
|
+
...item.revision === void 0 ? {} : { revision: item.revision }
|
|
10593
|
+
};
|
|
10594
|
+
});
|
|
10595
|
+
}
|
|
10596
|
+
/**
|
|
10597
|
+
* Every task dsh-mnemon delegates to a child Agent starts with one line of its own, such as
|
|
10598
|
+
* `[dsh-mnemon:delegated review]` (#393). DSH records the task as the child's prompt from a
|
|
10599
|
+
* person, so a plugin that reads sessions, such as one that collects a workspace's work, can
|
|
10600
|
+
* rely on this line, documented as stable, to leave Mnemon's own children out.
|
|
10601
|
+
*/
|
|
10602
|
+
const DELEGATED_TASK_PREFIX = "[dsh-mnemon:delegated ";
|
|
10603
|
+
function delegatedTaskLine(operation) {
|
|
10604
|
+
return `${DELEGATED_TASK_PREFIX}${operation}]`;
|
|
10605
|
+
}
|
|
10606
|
+
const WRITE_TOOLS = [
|
|
10607
|
+
...[
|
|
10608
|
+
"mnemon_memory_bodies",
|
|
10609
|
+
"mnemon_recall",
|
|
10610
|
+
"mnemon_related"
|
|
10611
|
+
],
|
|
10612
|
+
"mnemon_remember",
|
|
10613
|
+
"mnemon_link",
|
|
10614
|
+
"mnemon_forget",
|
|
10615
|
+
"mnemon_memory_body_create",
|
|
10616
|
+
"mnemon_memory_body_update",
|
|
10617
|
+
"mnemon_memory_body_merge"
|
|
10618
|
+
];
|
|
10619
|
+
const AUTONOMOUS_WRITE_TOOLS = WRITE_TOOLS.filter((tool) => tool !== "mnemon_forget");
|
|
10620
|
+
const EXPLICIT_WRITE_TOOLS = WRITE_TOOLS;
|
|
10621
|
+
const REVIEW_TOOLS = [
|
|
10622
|
+
...["mnemon_document_search"],
|
|
10623
|
+
"mnemon_runtime_memory",
|
|
10624
|
+
"mnemon_document_create"
|
|
10625
|
+
];
|
|
10626
|
+
const RESULT_TOOL_NAME = "mnemon_subagent_result";
|
|
10627
|
+
/** How many finished delegated children stay refused memory tools if DSH resumes one. */
|
|
10628
|
+
const FINISHED_CHILDREN = 1e3;
|
|
10629
|
+
const FINISHED_CHILD_REFUSAL = "This Mnemon task has finished, so it no longer reads or changes memory. Reply without memory tools.";
|
|
10630
|
+
const RESULT_TOOL_INPUT_SCHEMA = {
|
|
10631
|
+
type: "object",
|
|
10632
|
+
properties: {
|
|
10633
|
+
requestId: {
|
|
10634
|
+
type: "string",
|
|
10635
|
+
minLength: 1
|
|
10636
|
+
},
|
|
10637
|
+
result: {
|
|
10638
|
+
type: "object",
|
|
10639
|
+
additionalProperties: true
|
|
10640
|
+
}
|
|
10641
|
+
},
|
|
10642
|
+
required: ["requestId", "result"],
|
|
10643
|
+
additionalProperties: false
|
|
10644
|
+
};
|
|
10645
|
+
const RESULT_TOOL_OUTPUT_SCHEMA = {
|
|
10646
|
+
type: "object",
|
|
10647
|
+
properties: { recorded: {
|
|
10648
|
+
type: "boolean",
|
|
10649
|
+
const: true
|
|
10650
|
+
} },
|
|
10651
|
+
required: ["recorded"],
|
|
10652
|
+
additionalProperties: false
|
|
10653
|
+
};
|
|
10654
|
+
const WRITE_OPERATION_RESULT_TOOL = {
|
|
10655
|
+
remember: "mnemon_remember",
|
|
10656
|
+
"supervised-writeback": "mnemon_remember",
|
|
10657
|
+
link: "mnemon_link",
|
|
10658
|
+
forget: "mnemon_forget",
|
|
10659
|
+
"create-memory-body": "mnemon_memory_body_create",
|
|
10660
|
+
"update-memory-body": "mnemon_memory_body_update",
|
|
10661
|
+
"merge-memory-bodies": "mnemon_memory_body_merge"
|
|
10662
|
+
};
|
|
10663
|
+
var IdleReviewError = class extends Error {
|
|
10664
|
+
review;
|
|
10665
|
+
constructor(message, review, options) {
|
|
10666
|
+
super(message, options);
|
|
10667
|
+
this.review = review;
|
|
10668
|
+
this.name = "IdleReviewError";
|
|
10669
|
+
}
|
|
10670
|
+
};
|
|
10671
|
+
/** What a memory saved without a vector means for recall, and how its vector comes. */
|
|
10672
|
+
const UNEMBEDDED_HINT = "Saved without a vector, because embedding did not succeed: semantic matching misses it until its vector is filled in, while keyword, link and recency recall still reach it. Once the embedding service answers, the user fills it in from Test status in the Mnemon Native embedding settings, which runs through the storage root and embedding settings DSH uses; a terminal mnemon command may reach neither.";
|
|
10673
|
+
/** The memories a write saved without a vector: Mnemon Native says so with `embedded: false` (#388). */
|
|
10674
|
+
function withoutVector(receipts) {
|
|
10675
|
+
return receipts.filter((receipt) => isTool(receipt.name, "mnemon_remember")).flatMap((receipt) => {
|
|
10676
|
+
const value = optionalObject(receipt.value);
|
|
10677
|
+
if (value?.embedded !== false) return [];
|
|
10678
|
+
return [{
|
|
10679
|
+
...typeof value.memoryBodyId === "string" ? { memoryBodyId: value.memoryBodyId } : {},
|
|
10680
|
+
...typeof value.id === "string" ? { id: value.id } : {}
|
|
10681
|
+
}];
|
|
10682
|
+
});
|
|
10683
|
+
}
|
|
10684
|
+
/** A Memory Space write tool: each switches on the space it goes to. */
|
|
10685
|
+
const SPACE_WRITE_TOOLS = /* @__PURE__ */ new Set(["mnemon_remember", "mnemon_link"]);
|
|
10686
|
+
/**
|
|
10687
|
+
* Holds a delegated write to the Memory Space its caller named, and keeps a writer
|
|
10688
|
+
* from switching a space back on: a write or a link switches on the space it goes to,
|
|
10689
|
+
* so a space that was inactive when the run started takes one only when the caller
|
|
10690
|
+
* named it (#387). A space the writer creates is not in `inactive` and takes its first
|
|
10691
|
+
* write. Saving a memory never merges spaces: a merge switches spaces on and off, and
|
|
10692
|
+
* is a request of its own.
|
|
10693
|
+
*/
|
|
10694
|
+
function spaceWriteGuard(target, inactive) {
|
|
10695
|
+
return (execution) => {
|
|
10696
|
+
if (isTool(execution.name, "mnemon_memory_body_merge")) return "Saving a memory never merges Memory Spaces; a merge is a request of its own. Write the memory, or finish without writing.";
|
|
10697
|
+
const input = optionalObject(execution.arguments) ?? {};
|
|
10698
|
+
const space = typeof input.memoryBodyId === "string" ? input.memoryBodyId.trim() : void 0;
|
|
10699
|
+
if (target !== void 0) {
|
|
10700
|
+
if (execution.name !== void 0 && SPACE_WRITE_TOOLS.has(execution.name) && space !== target) return `This write was requested for Memory Space ${JSON.stringify(target)}. Call ${execution.name} with memoryBodyId ${JSON.stringify(target)}, or finish without writing and say why it does not fit.`;
|
|
10701
|
+
if (isTool(execution.name, "mnemon_memory_body_create", "mnemon_memory_body_update")) return `This write was requested for Memory Space ${JSON.stringify(target)}, so it creates or changes no space. Write there, or finish without writing and say why it does not fit.`;
|
|
10702
|
+
return;
|
|
10703
|
+
}
|
|
10704
|
+
if (space === void 0 || !inactive.has(space)) return void 0;
|
|
10705
|
+
if (execution.name !== void 0 && SPACE_WRITE_TOOLS.has(execution.name)) return `Memory Space ${JSON.stringify(space)} is switched off, and a write would switch it on. Write to an active space, or finish without writing and say which space would fit.`;
|
|
10706
|
+
if (isTool(execution.name, "mnemon_memory_body_update") && input.active === true) return `Memory Space ${JSON.stringify(space)} is switched off; only the user switches it on.`;
|
|
10707
|
+
};
|
|
10708
|
+
}
|
|
10709
|
+
/**
|
|
10710
|
+
* Why a write cannot go to the Memory Space its caller named, before a writer runs (#387):
|
|
10711
|
+
* the space is unknown, its Provider is off or takes no writes, or it is switched off and
|
|
10712
|
+
* the caller did not ask to switch it on. A model names a space by its description, the
|
|
10713
|
+
* judgement that misplaced memories before; switching a space back on is its own request.
|
|
10714
|
+
*/
|
|
10715
|
+
function targetProblem(spaces, target, activate) {
|
|
10716
|
+
const body = spaces.find((candidate) => candidate.id === target);
|
|
10717
|
+
if (body === void 0) {
|
|
10718
|
+
const known = spaces.slice(0, 20).map((candidate) => `${JSON.stringify(candidate.id)} (${candidate.name}${candidate.active ? "" : ", switched off"})`).join(", ");
|
|
10719
|
+
return `Memory Space ${JSON.stringify(target)} does not exist. Name one by its id${known === "" ? "; there is none yet." : `: ${known}.`}`;
|
|
10720
|
+
}
|
|
10721
|
+
if (body.providerEnabled === false) return `Memory Space ${JSON.stringify(target)} uses ${body.provider.label}, which is turned off on the dsh-mnemon page under Plugins. Name another space.`;
|
|
10722
|
+
if (body.provider.capabilities.remember !== true) return `Memory Space ${JSON.stringify(target)} (${body.provider.label}) does not take writes. Name another space.`;
|
|
10723
|
+
if (!body.active && !activate) return `Memory Space ${JSON.stringify(target)} (${body.name}) is switched off, and a write would switch it back on. If the user wants it back, switch it on first with mnemon_memory_body_update (active: true); otherwise name an active space.`;
|
|
10724
|
+
}
|
|
10725
|
+
/** Whether a write receipt put the memory in its space, or handed it to a Provider that confirms later. */
|
|
10303
10726
|
function writeLanded(value) {
|
|
10304
10727
|
if (mutationResultCommitted(value)) return true;
|
|
10305
10728
|
const state = [
|
|
@@ -10343,6 +10766,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10343
10766
|
/** Delegated children whose run has ended, newest last, at most FINISHED_CHILDREN. */
|
|
10344
10767
|
finishedChildren = /* @__PURE__ */ new Set();
|
|
10345
10768
|
disposed = false;
|
|
10769
|
+
reviewAdmission = new AutomaticReviewAdmission();
|
|
10346
10770
|
constructor(subagents, runtimeSource, resultRuntime, taskAgentModelResolver, runtimeMaintenanceMaxTokensResolver, runtimeMaintenanceTaskRunner) {
|
|
10347
10771
|
this.subagents = subagents;
|
|
10348
10772
|
this.runtimeSource = runtimeSource;
|
|
@@ -10363,6 +10787,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10363
10787
|
}]
|
|
10364
10788
|
},
|
|
10365
10789
|
execute: async (args, execution) => {
|
|
10790
|
+
if (this.reviewAdmission.acknowledge(args, execution)) return { recorded: true };
|
|
10366
10791
|
if (!isSubagent(execution.agent)) throw new Error("Mnemon subagent result tools are restricted to delegated children");
|
|
10367
10792
|
execution.signal.throwIfAborted();
|
|
10368
10793
|
assertDshOutputValue(RESULT_TOOL_INPUT_SCHEMA, args);
|
|
@@ -10377,6 +10802,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10377
10802
|
}
|
|
10378
10803
|
dispose() {
|
|
10379
10804
|
this.disposed = true;
|
|
10805
|
+
this.reviewAdmission.dispose();
|
|
10380
10806
|
this.resultRequests.clear();
|
|
10381
10807
|
const resumeGuard = this.disposeResumeGuard;
|
|
10382
10808
|
this.disposeResumeGuard = void 0;
|
|
@@ -10922,7 +11348,28 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
10922
11348
|
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
11349
|
}
|
|
10924
11350
|
}
|
|
10925
|
-
|
|
11351
|
+
/** Cheap scheduler admission: no model task when every review write target is unavailable. */
|
|
11352
|
+
canReview(parent) {
|
|
11353
|
+
return this.reviewTools(parent).some((tool) => tool !== "mnemon_document_search");
|
|
11354
|
+
}
|
|
11355
|
+
/** Automatic review admission; explicit user-requested reviews are unchanged. */
|
|
11356
|
+
checkReviewAccess(parent, signal) {
|
|
11357
|
+
return this.reviewAdmission.check(parent, signal);
|
|
11358
|
+
}
|
|
11359
|
+
availableTools(parent, tools) {
|
|
11360
|
+
const registry = parent.ctx?.tools;
|
|
11361
|
+
return typeof registry?.get === "function" ? tools.filter((name) => registry.get(name, parent) !== void 0) : [...tools];
|
|
11362
|
+
}
|
|
11363
|
+
reviewTools(parent) {
|
|
11364
|
+
const config = this.runtimeSource.config;
|
|
11365
|
+
if (!config.writeEnabled) return [];
|
|
11366
|
+
return this.availableTools(parent, REVIEW_TOOLS).filter((tool) => {
|
|
11367
|
+
if (tool === "mnemon_document_search") return true;
|
|
11368
|
+
const type = memoryTool(tool).sourceTypeId;
|
|
11369
|
+
return (tool !== "mnemon_runtime_memory" || config.idleReview.runtimeMemory) && allowsParticipation(config, type, "write", "automatic");
|
|
11370
|
+
});
|
|
11371
|
+
}
|
|
11372
|
+
async review(parent, signal, admission) {
|
|
10926
11373
|
const config = this.runtimeSource.config.idleReview;
|
|
10927
11374
|
if (idleReviewBlockReason(parent, config.agentTeams) !== void 0) return {
|
|
10928
11375
|
delegated: false,
|
|
@@ -10930,6 +11377,13 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
10930
11377
|
summary: "Idle review is paused by the Agent Teams compatibility setting.",
|
|
10931
11378
|
documentIds: []
|
|
10932
11379
|
};
|
|
11380
|
+
const skipped = {
|
|
11381
|
+
delegated: false,
|
|
11382
|
+
action: "skipped",
|
|
11383
|
+
summary: "Idle review has no applicable writable Source target.",
|
|
11384
|
+
documentIds: []
|
|
11385
|
+
};
|
|
11386
|
+
if (!this.canReview(parent)) return skipped;
|
|
10933
11387
|
let preferred = config.provider;
|
|
10934
11388
|
if (preferred === "fork") try {
|
|
10935
11389
|
this.provider("fork");
|
|
@@ -10942,24 +11396,40 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
10942
11396
|
};
|
|
10943
11397
|
preferred = "spawn";
|
|
10944
11398
|
}
|
|
10945
|
-
const
|
|
10946
|
-
|
|
10947
|
-
|
|
10948
|
-
|
|
10949
|
-
|
|
10950
|
-
|
|
10951
|
-
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
11399
|
+
const execution = await this.runtimeSource.executions.workflow(parent, "review", signal);
|
|
11400
|
+
try {
|
|
11401
|
+
if (admission !== void 0) {
|
|
11402
|
+
execution.signal.throwIfAborted();
|
|
11403
|
+
admission();
|
|
11404
|
+
}
|
|
11405
|
+
const view = execution.context.view;
|
|
11406
|
+
const instances = execution.graph.memoryComposition.generation(view.runtimeGeneration).sourceInstances();
|
|
11407
|
+
const tools = this.reviewTools(parent).filter((tool) => {
|
|
11408
|
+
if (tool === "mnemon_document_search") return true;
|
|
11409
|
+
const entry = memoryTool(tool);
|
|
11410
|
+
return view.actionOffers.some((offer) => offer.authority === void 0 && offer.sourceActionId === entry.operation && instances.some((instance) => instance.sourceInstanceKey === offer.sourceInstanceKey && instance.sourceTypeId === entry.sourceTypeId));
|
|
11411
|
+
});
|
|
11412
|
+
const writers = tools.filter((tool) => tool !== "mnemon_document_search");
|
|
11413
|
+
if (writers.length === 0) return skipped;
|
|
11414
|
+
const prompt = preferred === "fork" ? "Review the inherited completed checkpoint now." : reviewCheckpoint(parent.session, config.maxContextChars);
|
|
11415
|
+
const base = reviewPersona(tools.includes("mnemon_runtime_memory"));
|
|
11416
|
+
const persona = preferred === "fork" ? base : base.replaceAll("inherited", "supplied bounded");
|
|
11417
|
+
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);
|
|
11418
|
+
const value = object$6(result.structured);
|
|
11419
|
+
return {
|
|
11420
|
+
delegated: true,
|
|
11421
|
+
runId,
|
|
11422
|
+
provider,
|
|
11423
|
+
summary: typeof value.summary === "string" ? value.summary : "",
|
|
11424
|
+
action: typeof value.action === "string" ? value.action : "failed",
|
|
11425
|
+
memoryBodyIds: strings(value.memoryBodyIds),
|
|
11426
|
+
documentIds: strings(value.documentIds)
|
|
11427
|
+
};
|
|
11428
|
+
} finally {
|
|
11429
|
+
execution.release();
|
|
11430
|
+
}
|
|
10961
11431
|
}
|
|
10962
|
-
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route) {
|
|
11432
|
+
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route, admission) {
|
|
10963
11433
|
if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
|
|
10964
11434
|
const provider = this.provider(preferredProvider);
|
|
10965
11435
|
assertDshOutputSchema(outputSchema);
|
|
@@ -11076,19 +11546,25 @@ This is the only completion channel for this run. Do not finish with a plain-tex
|
|
|
11076
11546
|
model: fixed.model
|
|
11077
11547
|
};
|
|
11078
11548
|
const host = this.resultRuntime;
|
|
11079
|
-
const start = () => startWithContinuationTurns(host, parent, () =>
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11549
|
+
const start = () => startWithContinuationTurns(host, parent, () => {
|
|
11550
|
+
if (operation === "review") {
|
|
11551
|
+
signal.throwIfAborted();
|
|
11552
|
+
admission?.();
|
|
11553
|
+
}
|
|
11554
|
+
return startSubagent(this.subagents, host.agents, provider, {
|
|
11555
|
+
label,
|
|
11556
|
+
prompt: [{
|
|
11557
|
+
type: "text",
|
|
11558
|
+
text: `${delegatedTaskLine(operation)}\n${prompt}`
|
|
11559
|
+
}],
|
|
11560
|
+
parent,
|
|
11561
|
+
signal,
|
|
11562
|
+
...resolvedAgentOptions === void 0 ? {} : { agentOptions: resolvedAgentOptions },
|
|
11563
|
+
maxDepth: 1,
|
|
11564
|
+
toolFilter: { allow: [...this.availableTools(parent, tools), resultToolName] },
|
|
11565
|
+
persona: completionPersona
|
|
11566
|
+
}, host);
|
|
11567
|
+
});
|
|
11092
11568
|
run = operation === "review" ? await startGuardedReview(this.resultRuntime, parent, [...tools, resultToolName], start, (child) => {
|
|
11093
11569
|
reviewChildId = child.id;
|
|
11094
11570
|
}, reviewLayerPolicy()) : guard !== void 0 ? await startGuardedChild(this.resultRuntime, parent, start, guard, {
|
|
@@ -11276,7 +11752,7 @@ function bounded(value, maximum) {
|
|
|
11276
11752
|
if (typeof value !== "string") return void 0;
|
|
11277
11753
|
const normalized = value.replace(/\s+/gu, " ").trim();
|
|
11278
11754
|
if (normalized === "") return void 0;
|
|
11279
|
-
return normalized
|
|
11755
|
+
return truncate(normalized, maximum);
|
|
11280
11756
|
}
|
|
11281
11757
|
function item(value, index) {
|
|
11282
11758
|
const entry = object$5(value);
|
|
@@ -11430,337 +11906,223 @@ function emptyActivity(turn) {
|
|
|
11430
11906
|
/**
|
|
11431
11907
|
* Incremental durable-log projection. Repeated UI reads process only events
|
|
11432
11908
|
* appended since the previous snapshot instead of rescanning the full session.
|
|
11433
|
-
*/
|
|
11434
|
-
var TurnActivityProjection = class {
|
|
11435
|
-
eventCount = 0;
|
|
11436
|
-
lastEventSeq;
|
|
11437
|
-
pending = /* @__PURE__ */ new Map();
|
|
11438
|
-
byTurn = /* @__PURE__ */ new Map();
|
|
11439
|
-
reset() {
|
|
11440
|
-
this.eventCount = 0;
|
|
11441
|
-
this.lastEventSeq = void 0;
|
|
11442
|
-
this.pending.clear();
|
|
11443
|
-
this.byTurn.clear();
|
|
11444
|
-
}
|
|
11445
|
-
snapshot(events) {
|
|
11446
|
-
const currentLastSeq = events.at(-1)?.seq;
|
|
11447
|
-
if (events.length < this.eventCount || events.length === this.eventCount && this.lastEventSeq !== currentLastSeq) this.reset();
|
|
11448
|
-
for (let index = this.eventCount; index < events.length; index += 1) this.consume(events[index]);
|
|
11449
|
-
this.eventCount = events.length;
|
|
11450
|
-
this.lastEventSeq = currentLastSeq;
|
|
11451
|
-
return {
|
|
11452
|
-
cursor: typeof currentLastSeq === "number" ? currentLastSeq : events.length,
|
|
11453
|
-
activities: [...this.byTurn.values()].sort((left, right) => left.turn - right.turn).map((activity) => ({
|
|
11454
|
-
...activity,
|
|
11455
|
-
names: [...activity.names],
|
|
11456
|
-
retrieved: activity.retrieved.map((read) => ({
|
|
11457
|
-
...read,
|
|
11458
|
-
items: read.items.map((item) => ({ ...item }))
|
|
11459
|
-
})),
|
|
11460
|
-
writebacks: activity.writebacks.map((write) => ({
|
|
11461
|
-
...write,
|
|
11462
|
-
item: { ...write.item }
|
|
11463
|
-
}))
|
|
11464
|
-
}))
|
|
11465
|
-
};
|
|
11466
|
-
}
|
|
11467
|
-
consume(event) {
|
|
11468
|
-
if (event.type === "tool/call") {
|
|
11469
|
-
const turn = eventTurn$1(event);
|
|
11470
|
-
const callId = event.data.callId;
|
|
11471
|
-
const name = event.data.name;
|
|
11472
|
-
if (turn !== void 0 && typeof callId === "string" && typeof name === "string" && name.startsWith("mnemon_")) this.pending.set(callId, {
|
|
11473
|
-
turn,
|
|
11474
|
-
name
|
|
11475
|
-
});
|
|
11476
|
-
return;
|
|
11477
|
-
}
|
|
11478
|
-
if (event.type !== "tool/result") return;
|
|
11479
|
-
const callId = resultCallId(event);
|
|
11480
|
-
if (callId === void 0) return;
|
|
11481
|
-
const call = this.pending.get(callId);
|
|
11482
|
-
if (call === void 0) return;
|
|
11483
|
-
this.pending.delete(callId);
|
|
11484
|
-
let activity = this.byTurn.get(call.turn);
|
|
11485
|
-
if (activity === void 0) {
|
|
11486
|
-
activity = emptyActivity(call.turn);
|
|
11487
|
-
this.byTurn.set(call.turn, activity);
|
|
11488
|
-
}
|
|
11489
|
-
activity.count += 1;
|
|
11490
|
-
activity.names.push(call.name);
|
|
11491
|
-
if (event.data.error !== void 0) {
|
|
11492
|
-
activity.failures += 1;
|
|
11493
|
-
return;
|
|
11494
|
-
}
|
|
11495
|
-
const semantic = parseMnemonActivityMeta(event.data.meta, callId, call.name);
|
|
11496
|
-
if (semantic.read !== void 0) activity.retrieved.push(semantic.read);
|
|
11497
|
-
if (semantic.writeback !== void 0) activity.writebacks.push(semantic.writeback);
|
|
11498
|
-
const counted = counter(call.name);
|
|
11499
|
-
if (counted !== void 0) activity[counted] += 1;
|
|
11500
|
-
}
|
|
11501
|
-
};
|
|
11502
|
-
//#endregion
|
|
11503
|
-
//#region src/host/view-presentation.ts
|
|
11504
|
-
/**
|
|
11505
|
-
* The named tools bound to each Source type's Routes or Actions, from the tool table: these are
|
|
11506
|
-
* existing DSH product-tool bindings, not Core operation names. Tools that share one operation are
|
|
11507
|
-
* listed together, in table order, as the View's tool list names them.
|
|
11508
|
-
*/
|
|
11509
|
-
function namedBindings(kind) {
|
|
11510
|
-
const bindings = {};
|
|
11511
|
-
for (const [name, tool] of memoryTools()) {
|
|
11512
|
-
if (tool.sourceTypeId === void 0 || tool.operation === void 0 || tool.kind === "write" !== (kind === "action")) continue;
|
|
11513
|
-
const operations = bindings[tool.sourceTypeId] ??= {};
|
|
11514
|
-
const listed = operations[tool.operation];
|
|
11515
|
-
operations[tool.operation] = listed === void 0 ? name : `${listed} / ${name}`;
|
|
11516
|
-
}
|
|
11517
|
-
return bindings;
|
|
11518
|
-
}
|
|
11519
|
-
const ROUTE_TOOLS = namedBindings("route");
|
|
11520
|
-
const ACTION_TOOLS = namedBindings("action");
|
|
11521
|
-
const NAMED_SOURCE_TYPES = [.../* @__PURE__ */ new Set([...Object.keys(ROUTE_TOOLS), ...Object.keys(ACTION_TOOLS)])];
|
|
11522
|
-
/** Use a short availability list only where the Host has the matching named binding.
|
|
11523
|
-
* Unbound external operations retain exact ids and schemas in the generic envelope. */
|
|
11524
|
-
function modelMemoryWake(graph, turn) {
|
|
11525
|
-
const generation = graph.memoryComposition.generation(turn.view.runtimeGeneration);
|
|
11526
|
-
if (generation === void 0) throw new Error("Cannot present an unpinned Memory generation");
|
|
11527
|
-
return graph.composableTurns.memoryWake(turn.view.id, memoryWakeBindings(generation, turn.view));
|
|
11528
|
-
}
|
|
11529
|
-
function presentMemoryWake(generation, view) {
|
|
11530
|
-
return createMemoryWake(view, memoryWakeBindings(generation, view));
|
|
11531
|
-
}
|
|
11532
|
-
/**
|
|
11533
|
-
* Why a generic View tool names no id this turn's View offers, told apart (#390); undefined when
|
|
11534
|
-
* the id is offered. It names only what the MNEMON VIEW ROUTES envelope shows: an operation bound
|
|
11535
|
-
* to a named tool is offered through that tool, and the envelope does not list its id.
|
|
11536
|
-
*/
|
|
11537
|
-
function unofferedViewId(graph, turn, kind, id) {
|
|
11538
|
-
const view = turn.view;
|
|
11539
|
-
const offered = kind === "route" ? view.routes.map((route) => ({
|
|
11540
|
-
id: route.id,
|
|
11541
|
-
operation: route.sourceRouteId
|
|
11542
|
-
})) : view.actionOffers.map((offer) => ({
|
|
11543
|
-
id: offer.id,
|
|
11544
|
-
operation: offer.sourceActionId
|
|
11545
|
-
}));
|
|
11546
|
-
const label = kind === "route" ? "memory View Route" : "memory ActionOffer";
|
|
11547
|
-
if (typeof id !== "string" || id === "") return `${label} needs ${kind === "route" ? "routeId" : "offerId"}, the exact id of one it offers`;
|
|
11548
|
-
if (offered.some((entry) => entry.id === id)) return void 0;
|
|
11549
|
-
const generation = graph.memoryComposition.generation(view.runtimeGeneration);
|
|
11550
|
-
const bindings = generation === void 0 ? {} : memoryWakeBindings(generation, view);
|
|
11551
|
-
const named = (kind === "route" ? bindings.routes : bindings.actions) ?? {};
|
|
11552
|
-
const short = id.split("/").at(-1);
|
|
11553
|
-
const tools = [...new Set(offered.filter((entry) => named[entry.id] !== void 0 && (entry.operation === short || entry.id.split("/").at(-1) === short)).map((entry) => named[entry.id]))];
|
|
11554
|
-
if (tools.length > 0) return `${label} is unavailable: ${id}; this View offers it as ${tools.join(" and ")}, so call that tool`;
|
|
11555
|
-
const shown = offered.filter((entry) => named[entry.id] === void 0).map((entry) => entry.id);
|
|
11556
|
-
if (shown.length === 0) return `${label} is unavailable: ${id}; MNEMON VIEW ROUTES offers none by id this turn, only the named tools in MNEMON VIEW TOOLS`;
|
|
11557
|
-
const near = shown.filter((candidate) => candidate.split("/").at(-1) === short);
|
|
11558
|
-
if (near.length === 1) return `${label} is unavailable: ${id}; ids are exact as MNEMON VIEW ROUTES lists them: ${JSON.stringify(near[0])}`;
|
|
11559
|
-
const listed = [...near, ...shown.filter((candidate) => !near.includes(candidate))];
|
|
11560
|
-
return `${label} is unavailable: ${id}; MNEMON VIEW ROUTES offers ${listed.slice(0, 12).map((candidate) => JSON.stringify(candidate)).join(", ")}${listed.length > 12 ? ` and ${listed.length - 12} more` : ""}`;
|
|
11561
|
-
}
|
|
11562
|
-
function memoryWakeBindings(generation, view) {
|
|
11563
|
-
const instances = generation.sourceInstances();
|
|
11564
|
-
const routes = {}, actions = {};
|
|
11565
|
-
for (const typeId of NAMED_SOURCE_TYPES) {
|
|
11566
|
-
const selected = namedToolInstance(instances.filter((source) => source.sourceTypeId === typeId), typeId);
|
|
11567
|
-
if (selected === void 0) continue;
|
|
11568
|
-
for (const route of view.routes.filter((route) => route.sourceInstanceKey === selected.sourceInstanceKey)) {
|
|
11569
|
-
if (view.readGrants.find((grant) => grant.id === route.readGrantId)?.schema !== `dsh-mnemon.${typeId}/v1`) continue;
|
|
11570
|
-
const tool = ROUTE_TOOLS[typeId]?.[route.sourceRouteId];
|
|
11571
|
-
if (tool !== void 0) routes[route.id] = tool;
|
|
11572
|
-
}
|
|
11573
|
-
for (const action of view.actionOffers.filter((action) => action.sourceInstanceKey === selected.sourceInstanceKey)) {
|
|
11574
|
-
const tool = ACTION_TOOLS[typeId]?.[action.sourceActionId];
|
|
11575
|
-
if (tool !== void 0) actions[action.id] = tool;
|
|
11576
|
-
}
|
|
11577
|
-
}
|
|
11578
|
-
return {
|
|
11579
|
-
routes,
|
|
11580
|
-
actions
|
|
11581
|
-
};
|
|
11582
|
-
}
|
|
11583
|
-
/** Explicitly pick display fields; never copy grants, authority, or opaque Source state. */
|
|
11584
|
-
function inspectMemoryView(generation, view, state, turn, memoryText) {
|
|
11585
|
-
return structuredClone({
|
|
11586
|
-
id: view.id,
|
|
11587
|
-
digest: view.digest,
|
|
11588
|
-
generationId: view.runtimeGeneration,
|
|
11589
|
-
createdAt: view.createdAt,
|
|
11590
|
-
state,
|
|
11591
|
-
...turn === void 0 ? {} : { turn },
|
|
11592
|
-
strategyTypeId: view.strategyTypeId,
|
|
11593
|
-
strategyInstanceKey: view.strategyInstanceKey,
|
|
11594
|
-
extensions: view.strategyExtensions ?? [],
|
|
11595
|
-
projection: view.projection.map(({ id, sourceInstanceKey, mode, text, revision }) => ({
|
|
11596
|
-
id,
|
|
11597
|
-
sourceInstanceKey,
|
|
11598
|
-
mode,
|
|
11599
|
-
text,
|
|
11600
|
-
revision
|
|
11601
|
-
})),
|
|
11602
|
-
sourcePresentations: view.sourcePresentations ?? [],
|
|
11603
|
-
routes: view.routes.map(({ id, sourceInstanceKey, sourceRouteId, description, maxCalls }) => ({
|
|
11604
|
-
id,
|
|
11605
|
-
sourceInstanceKey,
|
|
11606
|
-
operationId: sourceRouteId,
|
|
11607
|
-
description,
|
|
11608
|
-
maxCalls
|
|
11609
|
-
})),
|
|
11610
|
-
actions: view.actionOffers.map(({ id, sourceInstanceKey, sourceActionId, description }) => ({
|
|
11611
|
-
id,
|
|
11612
|
-
sourceInstanceKey,
|
|
11613
|
-
operationId: sourceActionId,
|
|
11614
|
-
description
|
|
11615
|
-
})),
|
|
11616
|
-
memoryText: memoryText ?? presentMemoryWake(generation, view).text,
|
|
11617
|
-
...view.guidance === void 0 ? {} : { guidance: view.guidance },
|
|
11618
|
-
diagnostics: (view.diagnostics ?? []).map((diagnostic) => diagnostic.message)
|
|
11619
|
-
});
|
|
11620
|
-
}
|
|
11621
|
-
//#endregion
|
|
11622
|
-
//#region src/host/agent-memory-turn.ts
|
|
11623
|
-
/** The durable log, not a parent session id, identifies the executing turn. */
|
|
11624
|
-
function openAgentTurn(agent) {
|
|
11625
|
-
let open;
|
|
11626
|
-
for (const event of agent.session.snapshotEvents()) {
|
|
11627
|
-
const turn = typeof event.data.turn === "number" ? event.data.turn : void 0;
|
|
11628
|
-
if (event.type === "turn/start" && turn !== void 0) open = turn;
|
|
11629
|
-
else if (event.type === "turn/end" && turn === open) open = void 0;
|
|
11630
|
-
}
|
|
11631
|
-
return open;
|
|
11632
|
-
}
|
|
11633
|
-
/**
|
|
11634
|
-
* One Agent owns each turn pin. A child additionally retains its delegation
|
|
11635
|
-
* across parent completion, collection, and runtime swaps until it is disposed.
|
|
11636
|
-
*/
|
|
11637
|
-
var AgentMemoryTurn = class {
|
|
11638
|
-
agent;
|
|
11639
|
-
runtime;
|
|
11640
|
-
delegation;
|
|
11641
|
-
pinned;
|
|
11642
|
-
/** A turn that runs without memory because no composition was serving when it began. */
|
|
11643
|
-
unavailable;
|
|
11644
|
-
warned;
|
|
11645
|
-
pending;
|
|
11646
|
-
generation = 0;
|
|
11647
|
-
closed = false;
|
|
11648
|
-
lastInspection;
|
|
11649
|
-
lastWorkspace;
|
|
11650
|
-
releaseDelegation;
|
|
11651
|
-
constructor(agent, runtime, delegation) {
|
|
11652
|
-
this.agent = agent;
|
|
11653
|
-
this.runtime = runtime;
|
|
11654
|
-
this.delegation = delegation;
|
|
11655
|
-
if (delegation === void 0) return;
|
|
11656
|
-
this.releaseDelegation = runtime.executions.retain(agent, delegation);
|
|
11657
|
-
}
|
|
11658
|
-
get current() {
|
|
11659
|
-
return this.pinned;
|
|
11660
|
-
}
|
|
11661
|
-
/** Why the current turn has no View, when memory could not be composed for it. */
|
|
11662
|
-
get unavailableReason() {
|
|
11663
|
-
return this.unavailable?.reason;
|
|
11664
|
-
}
|
|
11665
|
-
inspect(workspaceRoot) {
|
|
11666
|
-
if (this.lastInspection === void 0) return void 0;
|
|
11667
|
-
if (workspaceRoot !== void 0 && resolve(workspaceRoot) !== this.lastWorkspace) return void 0;
|
|
11668
|
-
return structuredClone({
|
|
11669
|
-
...this.lastInspection,
|
|
11670
|
-
state: this.pinned === void 0 ? "recent" : "active"
|
|
11671
|
-
});
|
|
11672
|
-
}
|
|
11673
|
-
clearInspection() {
|
|
11674
|
-
this.lastInspection = void 0;
|
|
11675
|
-
this.lastWorkspace = void 0;
|
|
11909
|
+
*/
|
|
11910
|
+
var TurnActivityProjection = class {
|
|
11911
|
+
eventCount = 0;
|
|
11912
|
+
lastEventSeq;
|
|
11913
|
+
pending = /* @__PURE__ */ new Map();
|
|
11914
|
+
byTurn = /* @__PURE__ */ new Map();
|
|
11915
|
+
reset() {
|
|
11916
|
+
this.eventCount = 0;
|
|
11917
|
+
this.lastEventSeq = void 0;
|
|
11918
|
+
this.pending.clear();
|
|
11919
|
+
this.byTurn.clear();
|
|
11676
11920
|
}
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
if (this.
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
viewId: this.pinned.context.view.id
|
|
11684
|
-
};
|
|
11685
|
-
if (this.delegation !== void 0) return this.delegation;
|
|
11686
|
-
const graph = this.runtime.forAgent(this.agent);
|
|
11921
|
+
snapshot(events) {
|
|
11922
|
+
const currentLastSeq = events.at(-1)?.seq;
|
|
11923
|
+
if (events.length < this.eventCount || events.length === this.eventCount && this.lastEventSeq !== currentLastSeq) this.reset();
|
|
11924
|
+
for (let index = this.eventCount; index < events.length; index += 1) this.consume(events[index]);
|
|
11925
|
+
this.eventCount = events.length;
|
|
11926
|
+
this.lastEventSeq = currentLastSeq;
|
|
11687
11927
|
return {
|
|
11688
|
-
|
|
11689
|
-
|
|
11928
|
+
cursor: typeof currentLastSeq === "number" ? currentLastSeq : events.length,
|
|
11929
|
+
activities: [...this.byTurn.values()].sort((left, right) => left.turn - right.turn).map((activity) => ({
|
|
11930
|
+
...activity,
|
|
11931
|
+
names: [...activity.names],
|
|
11932
|
+
retrieved: activity.retrieved.map((read) => ({
|
|
11933
|
+
...read,
|
|
11934
|
+
items: read.items.map((item) => ({ ...item }))
|
|
11935
|
+
})),
|
|
11936
|
+
writebacks: activity.writebacks.map((write) => ({
|
|
11937
|
+
...write,
|
|
11938
|
+
item: { ...write.item }
|
|
11939
|
+
}))
|
|
11940
|
+
}))
|
|
11690
11941
|
};
|
|
11691
11942
|
}
|
|
11692
|
-
|
|
11693
|
-
if (
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
const generation = this.generation;
|
|
11699
|
-
const controller = new AbortController();
|
|
11700
|
-
const abort = () => controller.abort(signal.reason);
|
|
11701
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
11702
|
-
const result = this.pin(turn, generation, controller.signal).catch((error) => {
|
|
11703
|
-
if (!isMemoryCompositionUnavailable(error) || this.closed || this.generation !== generation) throw error;
|
|
11704
|
-
this.unavailable = {
|
|
11943
|
+
consume(event) {
|
|
11944
|
+
if (event.type === "tool/call") {
|
|
11945
|
+
const turn = eventTurn$1(event);
|
|
11946
|
+
const callId = event.data.callId;
|
|
11947
|
+
const name = event.data.name;
|
|
11948
|
+
if (turn !== void 0 && typeof callId === "string" && typeof name === "string" && name.startsWith("mnemon_")) this.pending.set(callId, {
|
|
11705
11949
|
turn,
|
|
11706
|
-
|
|
11707
|
-
};
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11950
|
+
name
|
|
11951
|
+
});
|
|
11952
|
+
return;
|
|
11953
|
+
}
|
|
11954
|
+
if (event.type !== "tool/result") return;
|
|
11955
|
+
const callId = resultCallId(event);
|
|
11956
|
+
if (callId === void 0) return;
|
|
11957
|
+
const call = this.pending.get(callId);
|
|
11958
|
+
if (call === void 0) return;
|
|
11959
|
+
this.pending.delete(callId);
|
|
11960
|
+
let activity = this.byTurn.get(call.turn);
|
|
11961
|
+
if (activity === void 0) {
|
|
11962
|
+
activity = emptyActivity(call.turn);
|
|
11963
|
+
this.byTurn.set(call.turn, activity);
|
|
11964
|
+
}
|
|
11965
|
+
activity.count += 1;
|
|
11966
|
+
activity.names.push(call.name);
|
|
11967
|
+
if (event.data.error !== void 0) {
|
|
11968
|
+
activity.failures += 1;
|
|
11969
|
+
return;
|
|
11970
|
+
}
|
|
11971
|
+
const semantic = parseMnemonActivityMeta(event.data.meta, callId, call.name);
|
|
11972
|
+
if (semantic.read !== void 0) activity.retrieved.push(semantic.read);
|
|
11973
|
+
if (semantic.writeback !== void 0) activity.writebacks.push(semantic.writeback);
|
|
11974
|
+
const counted = counter(call.name);
|
|
11975
|
+
if (counted !== void 0) activity[counted] += 1;
|
|
11976
|
+
}
|
|
11977
|
+
};
|
|
11978
|
+
//#endregion
|
|
11979
|
+
//#region src/host/idle-review-watermark.ts
|
|
11980
|
+
const MAX_STATE_BYTES = 16384;
|
|
11981
|
+
const MAX_TIMESTAMP = 864e13;
|
|
11982
|
+
function validAttempts(value) {
|
|
11983
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
11984
|
+
}
|
|
11985
|
+
function validTimestamp(value) {
|
|
11986
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= MAX_TIMESTAMP;
|
|
11987
|
+
}
|
|
11988
|
+
/** Messages deliberately omit filesystem paths, session ids and raw input. */
|
|
11989
|
+
function failure$2(error) {
|
|
11990
|
+
const code = error?.code;
|
|
11991
|
+
const detail = code === "EBUSY" ? " (busy)" : typeof code === "string" && /^[A-Z_]+$/u.test(code) ? ` (${code})` : "";
|
|
11992
|
+
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 });
|
|
11993
|
+
}
|
|
11994
|
+
/** Host-local admission state, deliberately outside Packs and Git sync. */
|
|
11995
|
+
var IdleReviewWatermarkStore = class {
|
|
11996
|
+
sessionId;
|
|
11997
|
+
directory;
|
|
11998
|
+
path;
|
|
11999
|
+
constructor(directory, sessionId) {
|
|
12000
|
+
this.sessionId = sessionId;
|
|
12001
|
+
if (!sessionId || Buffer.byteLength(sessionId) > 4096 || Buffer.byteLength(JSON.stringify(sessionId)) > 16128) throw failure$2(void 0);
|
|
12002
|
+
this.directory = join(resolve(directory), "state", "idle-review");
|
|
12003
|
+
this.path = join(this.directory, createHash("sha256").update(sessionId).digest("hex") + ".json");
|
|
12004
|
+
}
|
|
12005
|
+
load() {
|
|
12006
|
+
let descriptor;
|
|
11718
12007
|
try {
|
|
11719
|
-
|
|
12008
|
+
try {
|
|
12009
|
+
descriptor = fs.openSync(this.path, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
|
|
12010
|
+
} catch (error) {
|
|
12011
|
+
if (error.code === "ENOENT") return {
|
|
12012
|
+
version: 1,
|
|
12013
|
+
sessionId: this.sessionId,
|
|
12014
|
+
attempts: 0,
|
|
12015
|
+
lastReviewAttemptAt: null
|
|
12016
|
+
};
|
|
12017
|
+
throw error;
|
|
12018
|
+
}
|
|
12019
|
+
const info = fs.fstatSync(descriptor);
|
|
12020
|
+
if (!info.isFile() || info.size > MAX_STATE_BYTES) throw new Error("invalid state file");
|
|
12021
|
+
const bytes = Buffer.alloc(16385);
|
|
12022
|
+
let length = 0;
|
|
12023
|
+
while (length < bytes.length) {
|
|
12024
|
+
const count = fs.readSync(descriptor, bytes, length, bytes.length - length, null);
|
|
12025
|
+
if (count === 0) break;
|
|
12026
|
+
length += count;
|
|
12027
|
+
}
|
|
12028
|
+
if (length > MAX_STATE_BYTES) throw new Error("oversized state");
|
|
12029
|
+
const value = JSON.parse(bytes.subarray(0, length).toString("utf8"));
|
|
12030
|
+
if (value === null || typeof value !== "object") throw new Error("invalid state");
|
|
12031
|
+
const state = value;
|
|
12032
|
+
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");
|
|
12033
|
+
return {
|
|
12034
|
+
version: 1,
|
|
12035
|
+
sessionId: this.sessionId,
|
|
12036
|
+
attempts: state.attempts,
|
|
12037
|
+
lastReviewAttemptAt: state.lastReviewAttemptAt
|
|
12038
|
+
};
|
|
12039
|
+
} catch (error) {
|
|
12040
|
+
throw failure$2(error);
|
|
11720
12041
|
} finally {
|
|
11721
|
-
|
|
11722
|
-
|
|
12042
|
+
if (descriptor !== void 0) try {
|
|
12043
|
+
fs.closeSync(descriptor);
|
|
12044
|
+
} catch (error) {
|
|
12045
|
+
throw failure$2(error);
|
|
12046
|
+
}
|
|
11723
12047
|
}
|
|
11724
12048
|
}
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
this.
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
12049
|
+
/** Preserve already-used in-process quota on opt-in without admitting work. */
|
|
12050
|
+
restore(floor) {
|
|
12051
|
+
if (floor === void 0 || floor.attempts === 0 && floor.lastReviewAttemptAt === null) return this.load();
|
|
12052
|
+
return this.withLock(() => {
|
|
12053
|
+
const saved = this.load();
|
|
12054
|
+
const merged = this.merge(saved, floor);
|
|
12055
|
+
if (merged.attempts !== saved.attempts || merged.lastReviewAttemptAt !== saved.lastReviewAttemptAt) this.write(merged);
|
|
12056
|
+
return merged;
|
|
12057
|
+
});
|
|
11734
12058
|
}
|
|
11735
|
-
|
|
11736
|
-
if (
|
|
11737
|
-
this.
|
|
11738
|
-
|
|
12059
|
+
reserve(policy) {
|
|
12060
|
+
if (!validTimestamp(policy.now) || !validAttempts(policy.maxPerSession) || !validAttempts(policy.minIntervalMs)) throw failure$2(void 0);
|
|
12061
|
+
return this.withLock(() => {
|
|
12062
|
+
const saved = this.merge(this.load(), policy.floor);
|
|
12063
|
+
if (saved.attempts >= policy.maxPerSession || policy.now - (saved.lastReviewAttemptAt ?? -Infinity) < policy.minIntervalMs) return void 0;
|
|
12064
|
+
const next = {
|
|
12065
|
+
version: 1,
|
|
12066
|
+
sessionId: this.sessionId,
|
|
12067
|
+
attempts: saved.attempts + 1,
|
|
12068
|
+
lastReviewAttemptAt: policy.now
|
|
12069
|
+
};
|
|
12070
|
+
this.write(next);
|
|
12071
|
+
return next;
|
|
12072
|
+
});
|
|
12073
|
+
}
|
|
12074
|
+
merge(saved, floor) {
|
|
12075
|
+
if (floor === void 0) return saved;
|
|
12076
|
+
if (!validAttempts(floor.attempts) || (floor.attempts === 0 ? floor.lastReviewAttemptAt !== null : !validTimestamp(floor.lastReviewAttemptAt))) throw failure$2(void 0);
|
|
12077
|
+
const previous = Math.max(saved.lastReviewAttemptAt ?? -Infinity, floor.lastReviewAttemptAt ?? -Infinity);
|
|
12078
|
+
return {
|
|
12079
|
+
version: 1,
|
|
12080
|
+
sessionId: this.sessionId,
|
|
12081
|
+
attempts: Math.max(saved.attempts, floor.attempts),
|
|
12082
|
+
lastReviewAttemptAt: previous === -Infinity ? null : previous
|
|
12083
|
+
};
|
|
12084
|
+
}
|
|
12085
|
+
write(state) {
|
|
12086
|
+
const temporary = this.path + "." + randomUUID() + ".tmp";
|
|
11739
12087
|
try {
|
|
11740
|
-
|
|
12088
|
+
fs.writeFileSync(temporary, JSON.stringify(state) + "\n", {
|
|
12089
|
+
mode: 384,
|
|
12090
|
+
flag: "wx"
|
|
12091
|
+
});
|
|
12092
|
+
fs.renameSync(temporary, this.path);
|
|
11741
12093
|
} finally {
|
|
11742
|
-
|
|
12094
|
+
fs.rmSync(temporary, { force: true });
|
|
11743
12095
|
}
|
|
11744
12096
|
}
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
12097
|
+
/**
|
|
12098
|
+
* One bounded synchronous read/check/write critical section. Exclusive mkdir
|
|
12099
|
+
* protects independent Hosts too; contention fails closed without stale-lock
|
|
12100
|
+
* takeover. Never hold this lock while starting or awaiting model work.
|
|
12101
|
+
*/
|
|
12102
|
+
withLock(operation) {
|
|
12103
|
+
const lock = this.path + ".lock";
|
|
12104
|
+
let held = false;
|
|
11748
12105
|
try {
|
|
11749
|
-
|
|
11750
|
-
|
|
11751
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
12106
|
+
fs.mkdirSync(this.directory, {
|
|
12107
|
+
recursive: true,
|
|
12108
|
+
mode: 448
|
|
12109
|
+
});
|
|
12110
|
+
try {
|
|
12111
|
+
fs.mkdirSync(lock, { mode: 448 });
|
|
12112
|
+
} catch (error) {
|
|
12113
|
+
if (error.code === "EEXIST") throw Object.assign(/* @__PURE__ */ new Error("busy"), { code: "EBUSY" });
|
|
12114
|
+
throw error;
|
|
12115
|
+
}
|
|
12116
|
+
held = true;
|
|
12117
|
+
return operation();
|
|
11761
12118
|
} catch (error) {
|
|
11762
|
-
|
|
11763
|
-
|
|
12119
|
+
throw failure$2(error);
|
|
12120
|
+
} finally {
|
|
12121
|
+
if (held) try {
|
|
12122
|
+
fs.rmdirSync(lock);
|
|
12123
|
+
} catch (error) {
|
|
12124
|
+
throw failure$2(error);
|
|
12125
|
+
}
|
|
11764
12126
|
}
|
|
11765
12127
|
}
|
|
11766
12128
|
};
|
|
@@ -11858,6 +12220,7 @@ var MnemonAgentLifecycle = class {
|
|
|
11858
12220
|
config;
|
|
11859
12221
|
counters;
|
|
11860
12222
|
memoryTurn;
|
|
12223
|
+
runtimeDirectory;
|
|
11861
12224
|
primePending = true;
|
|
11862
12225
|
startSource;
|
|
11863
12226
|
guidedTurns = /* @__PURE__ */ new Set();
|
|
@@ -11874,10 +12237,21 @@ var MnemonAgentLifecycle = class {
|
|
|
11874
12237
|
idleReviewTimer;
|
|
11875
12238
|
reviewController;
|
|
11876
12239
|
reviewRunning = false;
|
|
12240
|
+
reviewAccessBlocked = false;
|
|
11877
12241
|
lastReviewAt;
|
|
11878
12242
|
lastReviewAction;
|
|
11879
12243
|
lastReviewScore;
|
|
11880
12244
|
lastReviewDocumentIds;
|
|
12245
|
+
disposed = false;
|
|
12246
|
+
reviewEpoch = 0;
|
|
12247
|
+
observedReviewPolicy;
|
|
12248
|
+
watermarkEnabled = false;
|
|
12249
|
+
/** Only this Agent's observed usage, attributed to the graph that composed each checkpoint. */
|
|
12250
|
+
reviewUsage = /* @__PURE__ */ new Map();
|
|
12251
|
+
watermarkKey;
|
|
12252
|
+
watermarkStore;
|
|
12253
|
+
watermarkError;
|
|
12254
|
+
restoredAttempts = 0;
|
|
11881
12255
|
idleReviewAttempts = 0;
|
|
11882
12256
|
lastReviewAttemptAt;
|
|
11883
12257
|
lastReviewFailure;
|
|
@@ -11885,15 +12259,18 @@ var MnemonAgentLifecycle = class {
|
|
|
11885
12259
|
lastAt;
|
|
11886
12260
|
lastError;
|
|
11887
12261
|
composedTurn;
|
|
11888
|
-
constructor(agent, coordinator, config, counters, source, memoryTurn) {
|
|
12262
|
+
constructor(agent, coordinator, config, counters, source, memoryTurn, runtimeDirectory) {
|
|
11889
12263
|
this.agent = agent;
|
|
11890
12264
|
this.coordinator = coordinator;
|
|
11891
12265
|
this.config = config;
|
|
11892
12266
|
this.counters = counters;
|
|
11893
12267
|
this.memoryTurn = memoryTurn;
|
|
12268
|
+
this.runtimeDirectory = runtimeDirectory;
|
|
11894
12269
|
this.startSource = source;
|
|
11895
12270
|
}
|
|
11896
12271
|
start() {
|
|
12272
|
+
this.restoreWatermark();
|
|
12273
|
+
this.observedReviewPolicy = this.reviewPolicy();
|
|
11897
12274
|
const disposers = [
|
|
11898
12275
|
this.agent.ctx.on("session/event", ((session, event) => this.sessionEvent(session, event))),
|
|
11899
12276
|
this.agent.ctx.on("system-prompt/assemble", ((assembly, context, next) => this.assemblePrompt(assembly, context, next))),
|
|
@@ -11901,12 +12278,14 @@ var MnemonAgentLifecycle = class {
|
|
|
11901
12278
|
this.agent.ctx.on("agent/turn-stopping", ((payload) => this.finishTurn(payload)))
|
|
11902
12279
|
];
|
|
11903
12280
|
return () => {
|
|
12281
|
+
this.disposed = true;
|
|
11904
12282
|
this.releaseView();
|
|
11905
12283
|
this.cancelIdleReview(true);
|
|
11906
12284
|
for (const dispose of disposers.reverse()) dispose();
|
|
11907
12285
|
};
|
|
11908
12286
|
}
|
|
11909
12287
|
snapshot() {
|
|
12288
|
+
this.restoreWatermark();
|
|
11910
12289
|
return {
|
|
11911
12290
|
sessionId: this.agent.id,
|
|
11912
12291
|
status: this.agent.status,
|
|
@@ -11918,8 +12297,14 @@ var MnemonAgentLifecycle = class {
|
|
|
11918
12297
|
reviewRunning: this.reviewRunning,
|
|
11919
12298
|
reviewActivity: this.reviewActivity(),
|
|
11920
12299
|
idleReviewAttempts: this.idleReviewAttempts,
|
|
11921
|
-
...
|
|
11922
|
-
|
|
12300
|
+
...this.config.idleReview.persistWatermark ? { idleReviewWatermark: {
|
|
12301
|
+
state: this.watermarkError === void 0 ? "ready" : "error",
|
|
12302
|
+
restoredAttempts: this.restoredAttempts,
|
|
12303
|
+
maxPerSession: this.config.idleReview.maxPerSession,
|
|
12304
|
+
...this.watermarkError === void 0 ? {} : { error: this.watermarkError }
|
|
12305
|
+
} } : {},
|
|
12306
|
+
...idleReviewBlockReason(this.agent, this.config.idleReview.agentTeams) !== void 0 ? { idleReviewBlocked: "agent-team" } : this.reviewAccessBlocked ? { idleReviewBlocked: "tool-access" } : {},
|
|
12307
|
+
...this.lastReviewAttemptAt === void 0 ? {} : { nextReviewAt: new Date(Math.min(864e13, this.lastReviewAttemptAt + this.config.idleReview.minIntervalMs)).toISOString() },
|
|
11923
12308
|
...this.lastReviewFailure === void 0 ? {} : { lastReviewFailure: this.lastReviewFailure },
|
|
11924
12309
|
lastPhase: this.lastPhase,
|
|
11925
12310
|
...this.lastReviewAt === void 0 ? {} : { lastReviewAt: this.lastReviewAt },
|
|
@@ -11987,6 +12372,7 @@ var MnemonAgentLifecycle = class {
|
|
|
11987
12372
|
this.releaseView(payload.turn);
|
|
11988
12373
|
return decision;
|
|
11989
12374
|
}
|
|
12375
|
+
if (!this.disposed && openAgentTurn(this.agent) === payload.turn) await this.memoryTurn?.begin(payload.turn, payload.signal);
|
|
11990
12376
|
if (!this.config.lifecycleEnabled) return decision;
|
|
11991
12377
|
if (this.config.writeEnabled && this.config.writebackMode === "guided") this.recordTurnMessages(payload.turn, decision.messages);
|
|
11992
12378
|
if (payload.step !== 1) return decision;
|
|
@@ -12030,7 +12416,35 @@ var MnemonAgentLifecycle = class {
|
|
|
12030
12416
|
this.memoryTurn?.end(turn);
|
|
12031
12417
|
}
|
|
12032
12418
|
async finishTurn(payload) {
|
|
12033
|
-
this.
|
|
12419
|
+
this.cancelIdleReview(true);
|
|
12420
|
+
this.recordComposedTurn(payload.turn);
|
|
12421
|
+
if (payload.signal.aborted || !this.restoreWatermark() || !this.idleReviewAllowed()) return;
|
|
12422
|
+
const activity = this.ensureTurnActivity(payload.turn);
|
|
12423
|
+
const tools = completedToolActivity(this.agent.session.snapshotEvents(), payload.turn);
|
|
12424
|
+
activity.toolCallCount = tools.count;
|
|
12425
|
+
activity.toolNames = tools.names;
|
|
12426
|
+
if (!this.reviewActivity().eligible || !this.reviewAdmitted(payload.turn)) return;
|
|
12427
|
+
const controller = new AbortController();
|
|
12428
|
+
const signal = AbortSignal.any([payload.signal, controller.signal]);
|
|
12429
|
+
const epoch = this.reviewEpoch;
|
|
12430
|
+
const policy = this.reviewPolicy();
|
|
12431
|
+
this.reviewController = controller;
|
|
12432
|
+
this.reviewRunning = true;
|
|
12433
|
+
let admitted = false;
|
|
12434
|
+
try {
|
|
12435
|
+
const available = await this.coordinator.checkReviewAccess(this.agent, signal);
|
|
12436
|
+
if (signal.aborted || epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed()) return;
|
|
12437
|
+
this.reviewAccessBlocked = !available;
|
|
12438
|
+
admitted = available;
|
|
12439
|
+
} catch (error) {
|
|
12440
|
+
if (!signal.aborted) this.fail(error);
|
|
12441
|
+
} finally {
|
|
12442
|
+
if (this.reviewController === controller) {
|
|
12443
|
+
this.reviewRunning = false;
|
|
12444
|
+
this.reviewController = void 0;
|
|
12445
|
+
}
|
|
12446
|
+
}
|
|
12447
|
+
if (admitted) this.scheduleIdleReview(payload.turn);
|
|
12034
12448
|
}
|
|
12035
12449
|
sessionEvent(session, event) {
|
|
12036
12450
|
if (session !== this.agent.session || event.type !== "turn/end") return;
|
|
@@ -12041,16 +12455,13 @@ var MnemonAgentLifecycle = class {
|
|
|
12041
12455
|
}
|
|
12042
12456
|
scheduleIdleReview(turn) {
|
|
12043
12457
|
this.cancelIdleReview(true);
|
|
12044
|
-
this.
|
|
12045
|
-
if (!this.idleReviewAllowed()) return;
|
|
12046
|
-
const activity = this.ensureTurnActivity(turn);
|
|
12047
|
-
const tools = completedToolActivity(this.agent.session.snapshotEvents(), turn);
|
|
12048
|
-
activity.toolCallCount = tools.count;
|
|
12049
|
-
activity.toolNames = tools.names;
|
|
12458
|
+
if (!this.restoreWatermark() || !this.idleReviewAllowed()) return;
|
|
12050
12459
|
if (!this.reviewActivity().eligible || !this.reviewAdmitted(turn)) return;
|
|
12460
|
+
const epoch = this.reviewEpoch;
|
|
12461
|
+
const policy = this.reviewPolicy();
|
|
12051
12462
|
this.idleReviewTimer = setTimeout(() => {
|
|
12052
12463
|
this.idleReviewTimer = void 0;
|
|
12053
|
-
if (!this.idleReviewAllowed() || this.reviewRunning) return;
|
|
12464
|
+
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed() || this.reviewRunning) return;
|
|
12054
12465
|
if (this.lastReviewAttemptAt !== void 0 && Date.now() < this.lastReviewAttemptAt + this.config.idleReview.minIntervalMs) {
|
|
12055
12466
|
this.scheduleIdleReview(turn);
|
|
12056
12467
|
return;
|
|
@@ -12058,7 +12469,73 @@ var MnemonAgentLifecycle = class {
|
|
|
12058
12469
|
if (this.agent.status !== "idle") return;
|
|
12059
12470
|
if (!this.agent.session.snapshotEvents().some((event) => event.type === "turn/end" && eventTurn(event) === turn) || !this.reviewActivity().eligible || !this.reviewAdmitted(turn)) return;
|
|
12060
12471
|
this.runIdleReview();
|
|
12061
|
-
}, Math.max(this.config.idleReviewMs, (this.lastReviewAttemptAt ?? -Infinity) + this.config.idleReview.minIntervalMs - Date.now()));
|
|
12472
|
+
}, Math.min(2147483647, Math.max(this.config.idleReviewMs, (this.lastReviewAttemptAt ?? -Infinity) + this.config.idleReview.minIntervalMs - Date.now())));
|
|
12473
|
+
}
|
|
12474
|
+
/** Resolve only the graph actually serving this parent, never a selected View or startup cwd. */
|
|
12475
|
+
restoreWatermark() {
|
|
12476
|
+
if (!this.config.idleReview.persistWatermark) {
|
|
12477
|
+
this.watermarkEnabled = false;
|
|
12478
|
+
return true;
|
|
12479
|
+
}
|
|
12480
|
+
try {
|
|
12481
|
+
const directory = this.runtimeDirectory?.();
|
|
12482
|
+
if (directory === void 0 || directory.trim() === "") throw new Error("runtime storage unavailable");
|
|
12483
|
+
const key = JSON.stringify([resolve(directory), this.agent.id]);
|
|
12484
|
+
if (this.watermarkKey === key && this.watermarkEnabled) return this.watermarkError === void 0;
|
|
12485
|
+
const sameRoot = this.watermarkKey === key;
|
|
12486
|
+
this.watermarkEnabled = true;
|
|
12487
|
+
this.watermarkKey = key;
|
|
12488
|
+
this.watermarkStore = new IdleReviewWatermarkStore(directory, this.agent.id);
|
|
12489
|
+
this.watermarkError = void 0;
|
|
12490
|
+
this.restoredAttempts = 0;
|
|
12491
|
+
const floor = this.reviewUsage.get(resolve(directory));
|
|
12492
|
+
if (!sameRoot) {
|
|
12493
|
+
this.idleReviewAttempts = floor?.attempts ?? 0;
|
|
12494
|
+
this.lastReviewAttemptAt = floor?.lastReviewAttemptAt ?? void 0;
|
|
12495
|
+
}
|
|
12496
|
+
const state = this.watermarkStore.restore(floor);
|
|
12497
|
+
this.restoredAttempts = state.attempts;
|
|
12498
|
+
this.idleReviewAttempts = state.attempts;
|
|
12499
|
+
this.lastReviewAttemptAt = state.lastReviewAttemptAt ?? void 0;
|
|
12500
|
+
this.reviewUsage.set(resolve(directory), state);
|
|
12501
|
+
return true;
|
|
12502
|
+
} catch (error) {
|
|
12503
|
+
this.pauseWatermark(error);
|
|
12504
|
+
return false;
|
|
12505
|
+
}
|
|
12506
|
+
}
|
|
12507
|
+
configurationChanged() {
|
|
12508
|
+
const policy = this.reviewPolicy();
|
|
12509
|
+
if (policy !== this.observedReviewPolicy) {
|
|
12510
|
+
this.cancelIdleReview(true);
|
|
12511
|
+
this.reviewAccessBlocked = false;
|
|
12512
|
+
}
|
|
12513
|
+
this.observedReviewPolicy = policy;
|
|
12514
|
+
this.restoreWatermark();
|
|
12515
|
+
}
|
|
12516
|
+
pauseWatermark(error) {
|
|
12517
|
+
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.";
|
|
12518
|
+
this.watermarkError = message;
|
|
12519
|
+
}
|
|
12520
|
+
/** Policy/root changes invalidate an already queued checkpoint instead of catching up. */
|
|
12521
|
+
reviewPolicy() {
|
|
12522
|
+
try {
|
|
12523
|
+
return JSON.stringify([
|
|
12524
|
+
this.config.idleReview.persistWatermark ? this.runtimeDirectory?.() : void 0,
|
|
12525
|
+
this.config.lifecycleEnabled,
|
|
12526
|
+
this.config.writeEnabled,
|
|
12527
|
+
this.config.storageScope,
|
|
12528
|
+
this.config.dataDir,
|
|
12529
|
+
this.config.writebackMode,
|
|
12530
|
+
this.config.idleReviewMs,
|
|
12531
|
+
this.config.idleReview,
|
|
12532
|
+
this.config.taskAgentModel,
|
|
12533
|
+
mainStrategyTypeId(this.config)
|
|
12534
|
+
]);
|
|
12535
|
+
} catch (error) {
|
|
12536
|
+
if (this.config.idleReview.persistWatermark) this.pauseWatermark(error);
|
|
12537
|
+
return;
|
|
12538
|
+
}
|
|
12062
12539
|
}
|
|
12063
12540
|
/** Which Strategy composed the turn, while its View is still pinned, and whether it declares idle review. */
|
|
12064
12541
|
recordComposedTurn(turn) {
|
|
@@ -12069,7 +12546,8 @@ var MnemonAgentLifecycle = class {
|
|
|
12069
12546
|
this.composedTurn = {
|
|
12070
12547
|
turn,
|
|
12071
12548
|
typeId: strategy.typeId,
|
|
12072
|
-
idleReview: strategyDrives(strategy, "idle-review")
|
|
12549
|
+
idleReview: strategyDrives(strategy, "idle-review"),
|
|
12550
|
+
directory: pinned.graph.directory
|
|
12073
12551
|
};
|
|
12074
12552
|
} else if (this.composedTurn?.turn !== turn) this.composedTurn = void 0;
|
|
12075
12553
|
}
|
|
@@ -12086,19 +12564,60 @@ var MnemonAgentLifecycle = class {
|
|
|
12086
12564
|
if (this.memoryTurn === void 0) return strategyDrives({ typeId }, "idle-review");
|
|
12087
12565
|
return this.composedTurn?.typeId === typeId && this.composedTurn.idleReview;
|
|
12088
12566
|
}
|
|
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();
|
|
12567
|
+
idleReviewAllowed(reserved = false) {
|
|
12568
|
+
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
12569
|
}
|
|
12092
12570
|
async runIdleReview() {
|
|
12093
12571
|
const controller = new AbortController();
|
|
12094
12572
|
const triggeredScore = this.reviewActivity().score;
|
|
12095
|
-
this.
|
|
12096
|
-
|
|
12573
|
+
const epoch = this.reviewEpoch;
|
|
12574
|
+
const policy = this.reviewPolicy();
|
|
12097
12575
|
this.reviewRunning = true;
|
|
12098
12576
|
this.reviewController = controller;
|
|
12099
|
-
this.mark("review");
|
|
12100
12577
|
try {
|
|
12101
|
-
|
|
12578
|
+
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed()) controller.abort(/* @__PURE__ */ new Error("Automatic idle review admission changed"));
|
|
12579
|
+
controller.signal.throwIfAborted();
|
|
12580
|
+
if (this.config.idleReview.persistWatermark) try {
|
|
12581
|
+
if (!this.restoreWatermark() || this.watermarkStore === void 0 || !this.idleReviewAllowed()) return;
|
|
12582
|
+
const reserved = this.watermarkStore.reserve({
|
|
12583
|
+
...this.config.idleReview,
|
|
12584
|
+
now: Date.now(),
|
|
12585
|
+
floor: {
|
|
12586
|
+
attempts: this.idleReviewAttempts,
|
|
12587
|
+
lastReviewAttemptAt: this.lastReviewAttemptAt ?? null
|
|
12588
|
+
}
|
|
12589
|
+
});
|
|
12590
|
+
const state = reserved ?? this.watermarkStore.load();
|
|
12591
|
+
this.idleReviewAttempts = Math.max(this.idleReviewAttempts, state.attempts);
|
|
12592
|
+
this.lastReviewAttemptAt = Math.max(this.lastReviewAttemptAt ?? -Infinity, state.lastReviewAttemptAt ?? -Infinity);
|
|
12593
|
+
if (this.lastReviewAttemptAt === -Infinity) this.lastReviewAttemptAt = void 0;
|
|
12594
|
+
const directory = this.runtimeDirectory?.();
|
|
12595
|
+
if (directory !== void 0) this.reviewUsage.set(resolve(directory), {
|
|
12596
|
+
attempts: this.idleReviewAttempts,
|
|
12597
|
+
lastReviewAttemptAt: this.lastReviewAttemptAt ?? null
|
|
12598
|
+
});
|
|
12599
|
+
if (reserved === void 0) return;
|
|
12600
|
+
} catch (error) {
|
|
12601
|
+
this.pauseWatermark(error);
|
|
12602
|
+
return;
|
|
12603
|
+
}
|
|
12604
|
+
else {
|
|
12605
|
+
this.idleReviewAttempts += 1;
|
|
12606
|
+
this.lastReviewAttemptAt = Date.now();
|
|
12607
|
+
const directory = this.composedTurn?.directory;
|
|
12608
|
+
if (directory !== void 0) {
|
|
12609
|
+
const key = resolve(directory);
|
|
12610
|
+
this.reviewUsage.set(key, {
|
|
12611
|
+
attempts: (this.reviewUsage.get(key)?.attempts ?? 0) + 1,
|
|
12612
|
+
lastReviewAttemptAt: this.lastReviewAttemptAt
|
|
12613
|
+
});
|
|
12614
|
+
}
|
|
12615
|
+
}
|
|
12616
|
+
this.mark("review");
|
|
12617
|
+
const result = await this.coordinator.review(this.agent, controller.signal, () => {
|
|
12618
|
+
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed(true)) controller.abort(/* @__PURE__ */ new Error("Automatic idle review admission changed"));
|
|
12619
|
+
controller.signal.throwIfAborted();
|
|
12620
|
+
});
|
|
12102
12621
|
if (controller.signal.aborted) return;
|
|
12103
12622
|
this.lastReviewAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12104
12623
|
this.lastReviewAction = result.action;
|
|
@@ -12119,6 +12638,7 @@ var MnemonAgentLifecycle = class {
|
|
|
12119
12638
|
}
|
|
12120
12639
|
}
|
|
12121
12640
|
cancelIdleReview(abortRunning) {
|
|
12641
|
+
this.reviewEpoch += 1;
|
|
12122
12642
|
if (this.idleReviewTimer !== void 0) clearTimeout(this.idleReviewTimer);
|
|
12123
12643
|
this.idleReviewTimer = void 0;
|
|
12124
12644
|
if (abortRunning) this.reviewController?.abort();
|
|
@@ -12282,11 +12802,15 @@ var MnemonLifecycle = class {
|
|
|
12282
12802
|
return this.skillEvolution.reviewNow(this.liveAgent(sessionId));
|
|
12283
12803
|
}
|
|
12284
12804
|
start() {
|
|
12805
|
+
const stopChanged = this.runtimeSource?.onChanged?.(() => {
|
|
12806
|
+
for (const { lifecycle } of this.owners.values()) lifecycle.configurationChanged();
|
|
12807
|
+
});
|
|
12285
12808
|
const stopCreated = this.ctx.on("agent/created", (({ agent, source }) => {
|
|
12286
12809
|
this.install(agent, source);
|
|
12287
12810
|
}));
|
|
12288
12811
|
for (const agent of this.ctx.agents.roots()) this.install(agent, "adopted");
|
|
12289
12812
|
return () => {
|
|
12813
|
+
stopChanged?.();
|
|
12290
12814
|
stopCreated();
|
|
12291
12815
|
for (const dispose of [...this.children.values()].reverse()) dispose();
|
|
12292
12816
|
for (const owner of [...this.owners.values()].reverse()) owner.dispose();
|
|
@@ -12791,7 +13315,7 @@ var MnemonLifecycle = class {
|
|
|
12791
13315
|
}
|
|
12792
13316
|
if (!this.ctx.agents.roots().includes(agent)) return;
|
|
12793
13317
|
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);
|
|
13318
|
+
const lifecycle = new MnemonAgentLifecycle(agent, this.coordinator, this.config, this.counters, source, memoryTurn, () => this.runtimeSource?.forAgent(agent).directory);
|
|
12795
13319
|
let dispose;
|
|
12796
13320
|
dispose = agent.ctx.effect(() => {
|
|
12797
13321
|
const stop = lifecycle.start();
|
|
@@ -12857,7 +13381,21 @@ var MnemonLifecycle = class {
|
|
|
12857
13381
|
})));
|
|
12858
13382
|
stops.push(agent.ctx.on("agent/pre-step", (async (payload, next) => {
|
|
12859
13383
|
const decision = await next();
|
|
12860
|
-
if (decision.kind === "reject" || payload.signal.aborted)
|
|
13384
|
+
if (decision.kind === "reject" || payload.signal.aborted) {
|
|
13385
|
+
memory.end(payload.turn);
|
|
13386
|
+
return decision;
|
|
13387
|
+
}
|
|
13388
|
+
if (openAgentTurn(agent) !== payload.turn) return decision;
|
|
13389
|
+
const recovering = memory.current?.turn !== payload.turn;
|
|
13390
|
+
await memory.begin(payload.turn, payload.signal);
|
|
13391
|
+
const pinned = memory.current;
|
|
13392
|
+
if (recovering && payload.step === 1 && this.config.lifecycleEnabled && pinned !== void 0) {
|
|
13393
|
+
const wake = modelMemoryWake(pinned.graph, pinned.context);
|
|
13394
|
+
if (wake.text.trim() !== "") return {
|
|
13395
|
+
kind: "enter",
|
|
13396
|
+
messages: [...decision.messages, createPluginMessage(wake.text, "recall")]
|
|
13397
|
+
};
|
|
13398
|
+
}
|
|
12861
13399
|
return decision;
|
|
12862
13400
|
})));
|
|
12863
13401
|
stops.push(agent.ctx.on("session/event", ((session, event) => {
|
|
@@ -13678,8 +14216,6 @@ var TrajectoryWindow = class {
|
|
|
13678
14216
|
};
|
|
13679
14217
|
//#endregion
|
|
13680
14218
|
//#region src/host/skill-evolution.ts
|
|
13681
|
-
/** A new skill goes to the user's own DSH skills unless the page chose another root. */
|
|
13682
|
-
const DEFAULT_TARGET_ROOT = "user-dsh";
|
|
13683
14219
|
/** A conversation's turn bars read at most this many of its reviews. */
|
|
13684
14220
|
const ACTIVITY_REVIEWS = 50;
|
|
13685
14221
|
const WAITING_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -13701,6 +14237,7 @@ const BACKTEST_CONCURRENCY = 2;
|
|
|
13701
14237
|
*/
|
|
13702
14238
|
function retryingSource(source, attempts = 4) {
|
|
13703
14239
|
return {
|
|
14240
|
+
...source.scope === void 0 ? {} : { scope: source.scope },
|
|
13704
14241
|
read: (operation, input, signal) => source.read(operation, input, signal),
|
|
13705
14242
|
mutate: async (operation, input, signal) => {
|
|
13706
14243
|
for (let attempt = 1;; attempt++) try {
|
|
@@ -13714,6 +14251,35 @@ function retryingSource(source, attempts = 4) {
|
|
|
13714
14251
|
function message(error) {
|
|
13715
14252
|
return redact(error instanceof Error ? error.message : String(error));
|
|
13716
14253
|
}
|
|
14254
|
+
/** Resolve only roots supplied by this Source's scoped catalog, never the task's cwd. */
|
|
14255
|
+
function newSkillRoot(settings, source, catalog, explicit) {
|
|
14256
|
+
let root = explicit;
|
|
14257
|
+
if (root === void 0) {
|
|
14258
|
+
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.");
|
|
14259
|
+
root = settings.targetScope === "project" || settings.targetScope === "storage" && (source.scope?.storage === "workspace" || source.scope?.storage === "workspaces") ? "project-dsh" : "user-dsh";
|
|
14260
|
+
}
|
|
14261
|
+
const directory = catalog.roots.find((candidate) => candidate.kind === root)?.path;
|
|
14262
|
+
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.");
|
|
14263
|
+
if (root.startsWith("project-")) try {
|
|
14264
|
+
if (!statSync(catalog.projectRoot).isDirectory()) throw new Error("not a directory");
|
|
14265
|
+
} catch {
|
|
14266
|
+
throw new Error("The selected workspace project is unavailable; select an existing workspace or choose User in Skills settings.");
|
|
14267
|
+
}
|
|
14268
|
+
let parent = directory;
|
|
14269
|
+
try {
|
|
14270
|
+
for (;;) try {
|
|
14271
|
+
if (!statSync(parent).isDirectory()) throw new Error("not a directory");
|
|
14272
|
+
accessSync(parent, constants.W_OK | constants.X_OK);
|
|
14273
|
+
break;
|
|
14274
|
+
} catch (error) {
|
|
14275
|
+
if (error.code !== "ENOENT" || dirname(parent) === parent) throw error;
|
|
14276
|
+
parent = dirname(parent);
|
|
14277
|
+
}
|
|
14278
|
+
} catch {
|
|
14279
|
+
throw new Error(`Cannot create a skill in ${root}: its directory is not writable. Choose another destination or fix its permissions.`);
|
|
14280
|
+
}
|
|
14281
|
+
return root;
|
|
14282
|
+
}
|
|
13717
14283
|
/**
|
|
13718
14284
|
* The Host half of skill evolution: it captures each conversation's
|
|
13719
14285
|
* trajectory as turns end, decides when a window has earned a review, runs
|
|
@@ -13893,7 +14459,8 @@ var SkillEvolution = class {
|
|
|
13893
14459
|
annotations: []
|
|
13894
14460
|
};
|
|
13895
14461
|
try {
|
|
13896
|
-
const
|
|
14462
|
+
const catalog = await source.read("catalog", null, controller.signal);
|
|
14463
|
+
const active = catalog.skills.filter((skill) => skill.shadowedBy === void 0);
|
|
13897
14464
|
const openSuggestions = [...await source.read("suggestions", { status: [
|
|
13898
14465
|
"pending",
|
|
13899
14466
|
"drafting",
|
|
@@ -13940,6 +14507,7 @@ var SkillEvolution = class {
|
|
|
13940
14507
|
expiryDays: current.expiryDays,
|
|
13941
14508
|
maxPendingPerSession: current.maxPendingPerSession
|
|
13942
14509
|
};
|
|
14510
|
+
const targetRoot = policy.allowCreate && parsed.suggestions.some((suggestion) => suggestion.action === "create" && !active.some((skill) => skill.name === suggestion.skillName)) ? newSkillRoot(current, source, catalog) : void 0;
|
|
13943
14511
|
const result = await source.mutate("review-record", {
|
|
13944
14512
|
review: {
|
|
13945
14513
|
...base,
|
|
@@ -13949,7 +14517,10 @@ var SkillEvolution = class {
|
|
|
13949
14517
|
},
|
|
13950
14518
|
trajectory,
|
|
13951
14519
|
annotations: parsed.annotations,
|
|
13952
|
-
suggestions: parsed.suggestions
|
|
14520
|
+
suggestions: parsed.suggestions.map((suggestion) => suggestion.action === "create" && targetRoot !== void 0 ? {
|
|
14521
|
+
...suggestion,
|
|
14522
|
+
targetRoot
|
|
14523
|
+
} : suggestion),
|
|
13953
14524
|
cases: parsed.cases,
|
|
13954
14525
|
policy
|
|
13955
14526
|
});
|
|
@@ -13996,13 +14567,27 @@ var SkillEvolution = class {
|
|
|
13996
14567
|
*/
|
|
13997
14568
|
async draft(port, workspace, input) {
|
|
13998
14569
|
const source = retryingSource(port);
|
|
14570
|
+
const pack = await source.read("evidence-pack", {
|
|
14571
|
+
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
|
|
14572
|
+
...input.skill === void 0 ? {} : { skill: input.skill }
|
|
14573
|
+
});
|
|
14574
|
+
const previous = pack.suggestion?.draftId === void 0 ? (await source.read("drafts", {
|
|
14575
|
+
skill: pack.skillName,
|
|
14576
|
+
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId }
|
|
14577
|
+
})).find((draft) => draft.suggestionId === input.suggestionId) : (await source.read("draft", { id: pack.suggestion.draftId })).draft;
|
|
14578
|
+
const targetRoot = pack.action === "update" ? pack.skill.root : newSkillRoot(this.host.settings(), source, await source.read("catalog"), input.targetRoot ?? previous?.targetRoot ?? pack.suggestion?.targetRoot);
|
|
14579
|
+
const guidance = [...previous?.guidance ?? [], ...input.guidance?.trim() ? [input.guidance.trim()] : []].slice(-8);
|
|
13999
14580
|
if (input.suggestionId !== void 0) await source.mutate("suggestion-status", {
|
|
14000
14581
|
id: input.suggestionId,
|
|
14001
14582
|
status: "drafting"
|
|
14002
14583
|
});
|
|
14003
14584
|
this.background("skill draft", async (signal) => {
|
|
14004
14585
|
try {
|
|
14005
|
-
await this.writeDraft(source, workspace, input,
|
|
14586
|
+
await this.writeDraft(source, workspace, input, {
|
|
14587
|
+
pack,
|
|
14588
|
+
targetRoot,
|
|
14589
|
+
guidance
|
|
14590
|
+
}, signal);
|
|
14006
14591
|
} catch (error) {
|
|
14007
14592
|
if (input.suggestionId !== void 0) await source.mutate("suggestion-status", {
|
|
14008
14593
|
id: input.suggestionId,
|
|
@@ -14017,14 +14602,9 @@ var SkillEvolution = class {
|
|
|
14017
14602
|
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId }
|
|
14018
14603
|
};
|
|
14019
14604
|
}
|
|
14020
|
-
async writeDraft(source, workspace, input, signal) {
|
|
14021
|
-
const pack = await source.read("evidence-pack", {
|
|
14022
|
-
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
|
|
14023
|
-
...input.skill === void 0 ? {} : { skill: input.skill }
|
|
14024
|
-
}, signal);
|
|
14605
|
+
async writeDraft(source, workspace, input, prepared, signal) {
|
|
14025
14606
|
const settings = this.host.settings();
|
|
14026
|
-
const
|
|
14027
|
-
const targetRoot = input.targetRoot ?? DEFAULT_TARGET_ROOT;
|
|
14607
|
+
const { pack, targetRoot, guidance } = prepared;
|
|
14028
14608
|
let prompt = skillDraftPrompt(pack, guidance, targetRoot);
|
|
14029
14609
|
let draft;
|
|
14030
14610
|
for (let attempt = 0; draft === void 0; attempt++) {
|
|
@@ -15333,16 +15913,59 @@ function documentActionReceipt(receipt) {
|
|
|
15333
15913
|
}
|
|
15334
15914
|
/**
|
|
15335
15915
|
* Named product tools are views over the same Source Route/Action protocol. Each family registers its tools in the
|
|
15336
|
-
*
|
|
15337
|
-
*
|
|
15916
|
+
* historical registry order, which puts Memory Spaces' reads before the Documents tools and its writes after
|
|
15917
|
+
* the runtime memory tool. DSH's prompt assembler independently orders wire schemas.
|
|
15338
15918
|
*/
|
|
15339
|
-
function registerTools(ctx, runtimeSource, coordinator) {
|
|
15919
|
+
function registerTools(ctx, runtimeSource, coordinator, contributions, initialReady) {
|
|
15920
|
+
const definitions = [];
|
|
15921
|
+
const catalog = { tools: { register: (tool) => {
|
|
15922
|
+
definitions.push(tool);
|
|
15923
|
+
} } };
|
|
15340
15924
|
const helpers = toolHelpers(runtimeSource);
|
|
15341
|
-
registerViewTools(
|
|
15342
|
-
registerMemorySpaceReadTools(
|
|
15343
|
-
registerDocumentTools(
|
|
15344
|
-
registerRuntimeTools(
|
|
15345
|
-
registerMemorySpaceWriteTools(
|
|
15925
|
+
registerViewTools(catalog, helpers, coordinator);
|
|
15926
|
+
registerMemorySpaceReadTools(catalog, helpers, coordinator);
|
|
15927
|
+
registerDocumentTools(catalog, helpers, coordinator);
|
|
15928
|
+
registerRuntimeTools(catalog, helpers, coordinator);
|
|
15929
|
+
registerMemorySpaceWriteTools(catalog, helpers, coordinator);
|
|
15930
|
+
const registered = /* @__PURE__ */ new Map();
|
|
15931
|
+
let active = true;
|
|
15932
|
+
let ready = initialReady === void 0;
|
|
15933
|
+
const reconcile = (snapshot) => {
|
|
15934
|
+
const types = new Set(snapshot.sources.map((source) => source.definition.manifest.typeId));
|
|
15935
|
+
for (const tool of definitions) {
|
|
15936
|
+
const type = memoryTool(tool.name)?.sourceTypeId;
|
|
15937
|
+
if (!(type === void 0 || ready && types.has(type))) {
|
|
15938
|
+
registered.get(tool.name)?.();
|
|
15939
|
+
registered.delete(tool.name);
|
|
15940
|
+
} else if (!registered.has(tool.name)) {
|
|
15941
|
+
const dispose = ctx.tools.register(tool);
|
|
15942
|
+
if (typeof dispose !== "function") throw new Error("dsh-mnemon tool registration did not return a disposer: " + tool.name);
|
|
15943
|
+
registered.set(tool.name, dispose);
|
|
15944
|
+
}
|
|
15945
|
+
}
|
|
15946
|
+
};
|
|
15947
|
+
const unsubscribe = contributions.onCommittedContributionsChanged(reconcile);
|
|
15948
|
+
const dispose = () => {
|
|
15949
|
+
if (!active) return;
|
|
15950
|
+
active = false;
|
|
15951
|
+
unsubscribe();
|
|
15952
|
+
for (const release of registered.values()) release();
|
|
15953
|
+
registered.clear();
|
|
15954
|
+
};
|
|
15955
|
+
try {
|
|
15956
|
+
reconcile(contributions.committedContributionSnapshot());
|
|
15957
|
+
} catch (error) {
|
|
15958
|
+
dispose();
|
|
15959
|
+
throw error;
|
|
15960
|
+
}
|
|
15961
|
+
initialReady?.then(() => {
|
|
15962
|
+
if (!active) return;
|
|
15963
|
+
ready = true;
|
|
15964
|
+
reconcile(contributions.committedContributionSnapshot());
|
|
15965
|
+
}).catch((error) => {
|
|
15966
|
+
if (active) console.warn("dsh-mnemon: could not initialize Source tool families", error);
|
|
15967
|
+
});
|
|
15968
|
+
return dispose;
|
|
15346
15969
|
}
|
|
15347
15970
|
/** What the handlers of every family share: the calling Agent's runtime graph and Sources, its checks, and a Source Action call. */
|
|
15348
15971
|
function toolHelpers(runtimeSource) {
|
|
@@ -18274,7 +18897,10 @@ function apply(rawContext, rawConfig = {}) {
|
|
|
18274
18897
|
runtime.dispose();
|
|
18275
18898
|
};
|
|
18276
18899
|
}, "dsh-mnemon.lifecycle-root()");
|
|
18277
|
-
|
|
18900
|
+
ctx.effect(() => {
|
|
18901
|
+
const loader = ctx.get("loader");
|
|
18902
|
+
return registerTools(ctx, runtime, coordinator, extensions, loader?.await?.());
|
|
18903
|
+
}, "dsh-mnemon: Source tool lifecycle");
|
|
18278
18904
|
registerCommands(ctx.commands, runtime, coordinator);
|
|
18279
18905
|
registerGuidance(ctx, resolved);
|
|
18280
18906
|
ctx.inject(["connection", "webServer"], (webContext) => {
|