dsh-mnemon 0.5.38 → 0.5.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/repair-legacy-session.mjs +86 -10
- package/lib/bundle.js +1 -1
- package/lib/client.js +78 -8
- package/lib/core.js +1 -1
- package/lib/index.js +703 -575
- package/lib/{runtime-0u6dOiF9.js → runtime-DmL0It3P.js} +43 -11
- package/lib/{starter-resolution-XVFzvnV0.js → starter-resolution-C5nNk-cs.js} +3 -3
- package/lib/starter.js +1 -1
- package/lib/testing.js +1 -1
- package/lib/types/src/client/locales.d.ts +10 -0
- package/lib/types/src/core/contracts/view.d.ts +12 -1
- package/lib/types/src/host/protocol.d.ts +8 -1
- package/package.json +37 -37
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-DmL0It3P.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";
|
|
@@ -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
|
*/
|
|
@@ -387,6 +387,7 @@ const DEFAULT_SKILLS_CONFIG = Object.freeze({
|
|
|
387
387
|
evolution: false,
|
|
388
388
|
allowCreate: true,
|
|
389
389
|
allowUpdate: true,
|
|
390
|
+
targetScope: "user",
|
|
390
391
|
autoBacktest: true,
|
|
391
392
|
hints: true,
|
|
392
393
|
sidebar: false,
|
|
@@ -474,6 +475,11 @@ const SkillsConfig = z.object({
|
|
|
474
475
|
evolution: z.boolean().default(DEFAULT_SKILLS_CONFIG.evolution),
|
|
475
476
|
allowCreate: z.boolean().default(DEFAULT_SKILLS_CONFIG.allowCreate),
|
|
476
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),
|
|
477
483
|
autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
|
|
478
484
|
hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
|
|
479
485
|
sidebar: z.boolean().default(DEFAULT_SKILLS_CONFIG.sidebar),
|
|
@@ -5331,7 +5337,7 @@ var MnemonGitSync = class {
|
|
|
5331
5337
|
if (committed) {
|
|
5332
5338
|
const identity = await this.commitIdentity(settings, signal);
|
|
5333
5339
|
const result = await this.git([
|
|
5334
|
-
...identity,
|
|
5340
|
+
...identity.args,
|
|
5335
5341
|
"commit",
|
|
5336
5342
|
"--quiet",
|
|
5337
5343
|
"-m",
|
|
@@ -5340,7 +5346,8 @@ var MnemonGitSync = class {
|
|
|
5340
5346
|
settings.subdir
|
|
5341
5347
|
], {
|
|
5342
5348
|
cwd: this.store.mirror(),
|
|
5343
|
-
signal
|
|
5349
|
+
signal,
|
|
5350
|
+
env: identity.env
|
|
5344
5351
|
});
|
|
5345
5352
|
if (result.exitCode !== 0) throw new Error("git commit failed: " + tail(result.stderr || result.stdout));
|
|
5346
5353
|
}
|
|
@@ -5373,20 +5380,35 @@ var MnemonGitSync = class {
|
|
|
5373
5380
|
};
|
|
5374
5381
|
}
|
|
5375
5382
|
/**
|
|
5376
|
-
* The
|
|
5377
|
-
*
|
|
5378
|
-
*
|
|
5379
|
-
*
|
|
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.
|
|
5380
5387
|
*/
|
|
5381
5388
|
async commitIdentity(settings, signal) {
|
|
5382
|
-
const
|
|
5383
|
-
const
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
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
|
+
};
|
|
5390
5412
|
}
|
|
5391
5413
|
/** One Git setting as this machine resolves it, or nothing when no config sets it. */
|
|
5392
5414
|
async configured(key, signal) {
|
|
@@ -6117,7 +6139,7 @@ var MnemonGitSync = class {
|
|
|
6117
6139
|
const failure = rejectedPush(output) ? "push-rejected" : REMOTE_FAILURE[remoteIssueOf(output)];
|
|
6118
6140
|
return {
|
|
6119
6141
|
pushed: false,
|
|
6120
|
-
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,
|
|
6121
6143
|
...failure === void 0 ? {} : { failure }
|
|
6122
6144
|
};
|
|
6123
6145
|
}
|
|
@@ -6279,7 +6301,10 @@ var MnemonGitSync = class {
|
|
|
6279
6301
|
try {
|
|
6280
6302
|
return await this.run(GIT_LABEL, prepared.args, {
|
|
6281
6303
|
timeoutMs: options.timeoutMs ?? 12e4,
|
|
6282
|
-
env: gitEnvironment(
|
|
6304
|
+
env: gitEnvironment({
|
|
6305
|
+
...process.env,
|
|
6306
|
+
...options.env
|
|
6307
|
+
}),
|
|
6283
6308
|
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
6284
6309
|
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
6285
6310
|
...options.maxOutputBytes === void 0 ? {} : { maxOutputBytes: options.maxOutputBytes },
|
|
@@ -7075,7 +7100,7 @@ var ComposableMemoryTurnManager = class {
|
|
|
7075
7100
|
const stored = this.requireTurn(turnId);
|
|
7076
7101
|
const operation = this.generations.acquire(stored.lease.id);
|
|
7077
7102
|
try {
|
|
7078
|
-
return await operation.generation.executeAction(stored.context.view, offerId, input, authorize, signal);
|
|
7103
|
+
return await operation.generation.executeAction(stored.context.view, offerId, input, authorize, signal, stored.context);
|
|
7079
7104
|
} finally {
|
|
7080
7105
|
operation.release();
|
|
7081
7106
|
}
|
|
@@ -10169,201 +10194,486 @@ async function assertAutomaticMemoryWrite(host, parent) {
|
|
|
10169
10194
|
return host.writableSourceFor(parent, "memory-spaces", "remember");
|
|
10170
10195
|
}
|
|
10171
10196
|
//#endregion
|
|
10172
|
-
//#region src/host/
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
*
|
|
10177
|
-
* permission failures, and an unavailable checkpoint is never a permanent ban.
|
|
10197
|
+
//#region src/host/view-presentation.ts
|
|
10198
|
+
/**
|
|
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
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
if (typeof args !== "object" || args === null || !("requestId" in args) || typeof args.requestId !== "string") return false;
|
|
10187
|
-
const challenge = this.challenges.get(args.requestId);
|
|
10188
|
-
if (challenge === void 0 || challenge.agent !== execution.agent || challenge.execution !== void 0) return false;
|
|
10189
|
-
execution.signal.throwIfAborted();
|
|
10190
|
-
challenge.execution = execution;
|
|
10191
|
-
return true;
|
|
10192
|
-
}
|
|
10193
|
-
dispose() {
|
|
10194
|
-
this.disposed = true;
|
|
10195
|
-
this.challenges.clear();
|
|
10196
|
-
for (const controller of this.controllers) controller.abort();
|
|
10197
|
-
}
|
|
10198
|
-
async check(agent, signal) {
|
|
10199
|
-
signal.throwIfAborted();
|
|
10200
|
-
const tools = agent.ctx.tools;
|
|
10201
|
-
if (this.disposed || tools?.execute === void 0 || this.draining.has(agent)) return false;
|
|
10202
|
-
const controller = new AbortController();
|
|
10203
|
-
this.controllers.add(controller);
|
|
10204
|
-
const combined = AbortSignal.any([signal, controller.signal]);
|
|
10205
|
-
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
10206
|
-
timeout.unref?.();
|
|
10207
|
-
let rejectAbort;
|
|
10208
|
-
const aborted = new Promise((_resolve, reject) => {
|
|
10209
|
-
rejectAbort = () => reject(combined.reason);
|
|
10210
|
-
combined.addEventListener("abort", rejectAbort, { once: true });
|
|
10211
|
-
});
|
|
10212
|
-
const attempt = async (codeMode) => {
|
|
10213
|
-
combined.throwIfAborted();
|
|
10214
|
-
const requestId = randomUUID();
|
|
10215
|
-
const callId = randomUUID();
|
|
10216
|
-
const challenge = {
|
|
10217
|
-
agent,
|
|
10218
|
-
accepted: false
|
|
10219
|
-
};
|
|
10220
|
-
this.challenges.set(requestId, challenge);
|
|
10221
|
-
const stops = [];
|
|
10222
|
-
let executing = false;
|
|
10223
|
-
const cleanup = () => {
|
|
10224
|
-
for (const stop of stops.reverse()) stop();
|
|
10225
|
-
this.draining.delete(agent);
|
|
10226
|
-
};
|
|
10227
|
-
try {
|
|
10228
|
-
stops.push(agent.ctx.on("tools/result", ((execution, result) => {
|
|
10229
|
-
if (execution === challenge.execution) challenge.accepted = result.isError === false;
|
|
10230
|
-
})));
|
|
10231
|
-
stops.push(agent.ctx.on("tools/pre-execute", (async (execution, next) => {
|
|
10232
|
-
const decision = await next();
|
|
10233
|
-
return execution.agent === agent && (execution.callId === callId || execution.rootCallId === callId) && decision.kind === "ask" ? { kind: "cancel" } : decision;
|
|
10234
|
-
}), { prepend: true }));
|
|
10235
|
-
const args = {
|
|
10236
|
-
requestId,
|
|
10237
|
-
result: {}
|
|
10238
|
-
};
|
|
10239
|
-
const pending = tools.execute({
|
|
10240
|
-
callId,
|
|
10241
|
-
agent,
|
|
10242
|
-
signal: combined,
|
|
10243
|
-
name: codeMode ? "run_code" : RESULT_TOOL,
|
|
10244
|
-
arguments: codeMode ? {
|
|
10245
|
-
description: "Check automatic memory review availability",
|
|
10246
|
-
code: `await tools.mnemon_subagent_result(${JSON.stringify(args)})`
|
|
10247
|
-
} : args
|
|
10248
|
-
});
|
|
10249
|
-
executing = true;
|
|
10250
|
-
this.draining.add(agent);
|
|
10251
|
-
const settled = pending.finally(cleanup);
|
|
10252
|
-
const outcome = await Promise.race([settled, aborted]);
|
|
10253
|
-
combined.throwIfAborted();
|
|
10254
|
-
return challenge.execution !== void 0 && challenge.accepted && outcome.isError === false;
|
|
10255
|
-
} finally {
|
|
10256
|
-
this.challenges.delete(requestId);
|
|
10257
|
-
if (!executing) cleanup();
|
|
10258
|
-
}
|
|
10259
|
-
};
|
|
10260
|
-
try {
|
|
10261
|
-
if (await attempt(false)) return true;
|
|
10262
|
-
return tools.get?.("run_code", agent) !== void 0 && await attempt(true);
|
|
10263
|
-
} catch {
|
|
10264
|
-
signal.throwIfAborted();
|
|
10265
|
-
return false;
|
|
10266
|
-
} finally {
|
|
10267
|
-
clearTimeout(timeout);
|
|
10268
|
-
if (rejectAbort !== void 0) combined.removeEventListener("abort", rejectAbort);
|
|
10269
|
-
this.controllers.delete(controller);
|
|
10270
|
-
}
|
|
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}`;
|
|
10271
10210
|
}
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
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));
|
|
10285
10225
|
}
|
|
10286
10226
|
/**
|
|
10287
|
-
*
|
|
10288
|
-
*
|
|
10289
|
-
*
|
|
10290
|
-
* rely on this line, documented as stable, to leave Mnemon's own children out.
|
|
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.
|
|
10291
10230
|
*/
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
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` : ""}`;
|
|
10295
10255
|
}
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
];
|
|
10309
|
-
|
|
10310
|
-
const EXPLICIT_WRITE_TOOLS = WRITE_TOOLS;
|
|
10311
|
-
const REVIEW_TOOLS = [
|
|
10312
|
-
...["mnemon_document_search"],
|
|
10313
|
-
"mnemon_runtime_memory",
|
|
10314
|
-
"mnemon_document_create"
|
|
10315
|
-
];
|
|
10316
|
-
const RESULT_TOOL_NAME = "mnemon_subagent_result";
|
|
10317
|
-
/** How many finished delegated children stay refused memory tools if DSH resumes one. */
|
|
10318
|
-
const FINISHED_CHILDREN = 1e3;
|
|
10319
|
-
const FINISHED_CHILD_REFUSAL = "This Mnemon task has finished, so it no longer reads or changes memory. Reply without memory tools.";
|
|
10320
|
-
const RESULT_TOOL_INPUT_SCHEMA = {
|
|
10321
|
-
type: "object",
|
|
10322
|
-
properties: {
|
|
10323
|
-
requestId: {
|
|
10324
|
-
type: "string",
|
|
10325
|
-
minLength: 1
|
|
10326
|
-
},
|
|
10327
|
-
result: {
|
|
10328
|
-
type: "object",
|
|
10329
|
-
additionalProperties: true
|
|
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;
|
|
10266
|
+
}
|
|
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;
|
|
10330
10270
|
}
|
|
10331
|
-
},
|
|
10332
|
-
required: ["requestId", "result"],
|
|
10333
|
-
additionalProperties: false
|
|
10334
|
-
};
|
|
10335
|
-
const RESULT_TOOL_OUTPUT_SCHEMA = {
|
|
10336
|
-
type: "object",
|
|
10337
|
-
properties: { recorded: {
|
|
10338
|
-
type: "boolean",
|
|
10339
|
-
const: true
|
|
10340
|
-
} },
|
|
10341
|
-
required: ["recorded"],
|
|
10342
|
-
additionalProperties: false
|
|
10343
|
-
};
|
|
10344
|
-
const WRITE_OPERATION_RESULT_TOOL = {
|
|
10345
|
-
remember: "mnemon_remember",
|
|
10346
|
-
"supervised-writeback": "mnemon_remember",
|
|
10347
|
-
link: "mnemon_link",
|
|
10348
|
-
forget: "mnemon_forget",
|
|
10349
|
-
"create-memory-body": "mnemon_memory_body_create",
|
|
10350
|
-
"update-memory-body": "mnemon_memory_body_update",
|
|
10351
|
-
"merge-memory-bodies": "mnemon_memory_body_merge"
|
|
10352
|
-
};
|
|
10353
|
-
var IdleReviewError = class extends Error {
|
|
10354
|
-
review;
|
|
10355
|
-
constructor(message, review, options) {
|
|
10356
|
-
super(message, options);
|
|
10357
|
-
this.review = review;
|
|
10358
|
-
this.name = "IdleReviewError";
|
|
10359
10271
|
}
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10272
|
+
return {
|
|
10273
|
+
routes,
|
|
10274
|
+
actions
|
|
10275
|
+
};
|
|
10276
|
+
}
|
|
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
|
+
});
|
|
10314
|
+
}
|
|
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);
|
|
10367
10677
|
if (value?.embedded !== false) return [];
|
|
10368
10678
|
return [{
|
|
10369
10679
|
...typeof value.memoryBodyId === "string" ? { memoryBodyId: value.memoryBodyId } : {},
|
|
@@ -11442,7 +11752,7 @@ function bounded(value, maximum) {
|
|
|
11442
11752
|
if (typeof value !== "string") return void 0;
|
|
11443
11753
|
const normalized = value.replace(/\s+/gu, " ").trim();
|
|
11444
11754
|
if (normalized === "") return void 0;
|
|
11445
|
-
return normalized
|
|
11755
|
+
return truncate(normalized, maximum);
|
|
11446
11756
|
}
|
|
11447
11757
|
function item(value, index) {
|
|
11448
11758
|
const entry = object$5(value);
|
|
@@ -11573,361 +11883,96 @@ function counter(name) {
|
|
|
11573
11883
|
return tool.sourceTypeId === "documents" ? "documentSearches" : "recalls";
|
|
11574
11884
|
}
|
|
11575
11885
|
function eventTurn$1(event) {
|
|
11576
|
-
return typeof event.data.turn === "number" ? event.data.turn : void 0;
|
|
11577
|
-
}
|
|
11578
|
-
function resultCallId(event) {
|
|
11579
|
-
const message = event.data.message;
|
|
11580
|
-
return typeof message?.source?.callId === "string" && message.source.callId !== "" ? message.source.callId : void 0;
|
|
11581
|
-
}
|
|
11582
|
-
function emptyActivity(turn) {
|
|
11583
|
-
return {
|
|
11584
|
-
turn,
|
|
11585
|
-
count: 0,
|
|
11586
|
-
names: [],
|
|
11587
|
-
recalls: 0,
|
|
11588
|
-
writes: 0,
|
|
11589
|
-
documentSearches: 0,
|
|
11590
|
-
inspections: 0,
|
|
11591
|
-
failures: 0,
|
|
11592
|
-
retrieved: [],
|
|
11593
|
-
writebacks: []
|
|
11594
|
-
};
|
|
11595
|
-
}
|
|
11596
|
-
/**
|
|
11597
|
-
* Incremental durable-log projection. Repeated UI reads process only events
|
|
11598
|
-
* appended since the previous snapshot instead of rescanning the full session.
|
|
11599
|
-
*/
|
|
11600
|
-
var TurnActivityProjection = class {
|
|
11601
|
-
eventCount = 0;
|
|
11602
|
-
lastEventSeq;
|
|
11603
|
-
pending = /* @__PURE__ */ new Map();
|
|
11604
|
-
byTurn = /* @__PURE__ */ new Map();
|
|
11605
|
-
reset() {
|
|
11606
|
-
this.eventCount = 0;
|
|
11607
|
-
this.lastEventSeq = void 0;
|
|
11608
|
-
this.pending.clear();
|
|
11609
|
-
this.byTurn.clear();
|
|
11610
|
-
}
|
|
11611
|
-
snapshot(events) {
|
|
11612
|
-
const currentLastSeq = events.at(-1)?.seq;
|
|
11613
|
-
if (events.length < this.eventCount || events.length === this.eventCount && this.lastEventSeq !== currentLastSeq) this.reset();
|
|
11614
|
-
for (let index = this.eventCount; index < events.length; index += 1) this.consume(events[index]);
|
|
11615
|
-
this.eventCount = events.length;
|
|
11616
|
-
this.lastEventSeq = currentLastSeq;
|
|
11617
|
-
return {
|
|
11618
|
-
cursor: typeof currentLastSeq === "number" ? currentLastSeq : events.length,
|
|
11619
|
-
activities: [...this.byTurn.values()].sort((left, right) => left.turn - right.turn).map((activity) => ({
|
|
11620
|
-
...activity,
|
|
11621
|
-
names: [...activity.names],
|
|
11622
|
-
retrieved: activity.retrieved.map((read) => ({
|
|
11623
|
-
...read,
|
|
11624
|
-
items: read.items.map((item) => ({ ...item }))
|
|
11625
|
-
})),
|
|
11626
|
-
writebacks: activity.writebacks.map((write) => ({
|
|
11627
|
-
...write,
|
|
11628
|
-
item: { ...write.item }
|
|
11629
|
-
}))
|
|
11630
|
-
}))
|
|
11631
|
-
};
|
|
11632
|
-
}
|
|
11633
|
-
consume(event) {
|
|
11634
|
-
if (event.type === "tool/call") {
|
|
11635
|
-
const turn = eventTurn$1(event);
|
|
11636
|
-
const callId = event.data.callId;
|
|
11637
|
-
const name = event.data.name;
|
|
11638
|
-
if (turn !== void 0 && typeof callId === "string" && typeof name === "string" && name.startsWith("mnemon_")) this.pending.set(callId, {
|
|
11639
|
-
turn,
|
|
11640
|
-
name
|
|
11641
|
-
});
|
|
11642
|
-
return;
|
|
11643
|
-
}
|
|
11644
|
-
if (event.type !== "tool/result") return;
|
|
11645
|
-
const callId = resultCallId(event);
|
|
11646
|
-
if (callId === void 0) return;
|
|
11647
|
-
const call = this.pending.get(callId);
|
|
11648
|
-
if (call === void 0) return;
|
|
11649
|
-
this.pending.delete(callId);
|
|
11650
|
-
let activity = this.byTurn.get(call.turn);
|
|
11651
|
-
if (activity === void 0) {
|
|
11652
|
-
activity = emptyActivity(call.turn);
|
|
11653
|
-
this.byTurn.set(call.turn, activity);
|
|
11654
|
-
}
|
|
11655
|
-
activity.count += 1;
|
|
11656
|
-
activity.names.push(call.name);
|
|
11657
|
-
if (event.data.error !== void 0) {
|
|
11658
|
-
activity.failures += 1;
|
|
11659
|
-
return;
|
|
11660
|
-
}
|
|
11661
|
-
const semantic = parseMnemonActivityMeta(event.data.meta, callId, call.name);
|
|
11662
|
-
if (semantic.read !== void 0) activity.retrieved.push(semantic.read);
|
|
11663
|
-
if (semantic.writeback !== void 0) activity.writebacks.push(semantic.writeback);
|
|
11664
|
-
const counted = counter(call.name);
|
|
11665
|
-
if (counted !== void 0) activity[counted] += 1;
|
|
11666
|
-
}
|
|
11667
|
-
};
|
|
11668
|
-
//#endregion
|
|
11669
|
-
//#region src/host/view-presentation.ts
|
|
11670
|
-
/**
|
|
11671
|
-
* The named tools bound to each Source type's Routes or Actions, from the tool table: these are
|
|
11672
|
-
* existing DSH product-tool bindings, not Core operation names. Tools that share one operation are
|
|
11673
|
-
* listed together, in table order, as the View's tool list names them.
|
|
11674
|
-
*/
|
|
11675
|
-
function namedBindings(kind) {
|
|
11676
|
-
const bindings = {};
|
|
11677
|
-
for (const [name, tool] of memoryTools()) {
|
|
11678
|
-
if (tool.sourceTypeId === void 0 || tool.operation === void 0 || tool.kind === "write" !== (kind === "action")) continue;
|
|
11679
|
-
const operations = bindings[tool.sourceTypeId] ??= {};
|
|
11680
|
-
const listed = operations[tool.operation];
|
|
11681
|
-
operations[tool.operation] = listed === void 0 ? name : `${listed} / ${name}`;
|
|
11682
|
-
}
|
|
11683
|
-
return bindings;
|
|
11684
|
-
}
|
|
11685
|
-
const ROUTE_TOOLS = namedBindings("route");
|
|
11686
|
-
const ACTION_TOOLS = namedBindings("action");
|
|
11687
|
-
const NAMED_SOURCE_TYPES = [.../* @__PURE__ */ new Set([...Object.keys(ROUTE_TOOLS), ...Object.keys(ACTION_TOOLS)])];
|
|
11688
|
-
/** Use a short availability list only where the Host has the matching named binding.
|
|
11689
|
-
* Unbound external operations retain exact ids and schemas in the generic envelope. */
|
|
11690
|
-
function modelMemoryWake(graph, turn) {
|
|
11691
|
-
const generation = graph.memoryComposition.generation(turn.view.runtimeGeneration);
|
|
11692
|
-
if (generation === void 0) throw new Error("Cannot present an unpinned Memory generation");
|
|
11693
|
-
return graph.composableTurns.memoryWake(turn.view.id, memoryWakeBindings(generation, turn.view));
|
|
11694
|
-
}
|
|
11695
|
-
function presentMemoryWake(generation, view) {
|
|
11696
|
-
return createMemoryWake(view, memoryWakeBindings(generation, view));
|
|
11697
|
-
}
|
|
11698
|
-
/**
|
|
11699
|
-
* Why a generic View tool names no id this turn's View offers, told apart (#390); undefined when
|
|
11700
|
-
* the id is offered. It names only what the MNEMON VIEW ROUTES envelope shows: an operation bound
|
|
11701
|
-
* to a named tool is offered through that tool, and the envelope does not list its id.
|
|
11702
|
-
*/
|
|
11703
|
-
function unofferedViewId(graph, turn, kind, id) {
|
|
11704
|
-
const view = turn.view;
|
|
11705
|
-
const offered = kind === "route" ? view.routes.map((route) => ({
|
|
11706
|
-
id: route.id,
|
|
11707
|
-
operation: route.sourceRouteId
|
|
11708
|
-
})) : view.actionOffers.map((offer) => ({
|
|
11709
|
-
id: offer.id,
|
|
11710
|
-
operation: offer.sourceActionId
|
|
11711
|
-
}));
|
|
11712
|
-
const label = kind === "route" ? "memory View Route" : "memory ActionOffer";
|
|
11713
|
-
if (typeof id !== "string" || id === "") return `${label} needs ${kind === "route" ? "routeId" : "offerId"}, the exact id of one it offers`;
|
|
11714
|
-
if (offered.some((entry) => entry.id === id)) return void 0;
|
|
11715
|
-
const generation = graph.memoryComposition.generation(view.runtimeGeneration);
|
|
11716
|
-
const bindings = generation === void 0 ? {} : memoryWakeBindings(generation, view);
|
|
11717
|
-
const named = (kind === "route" ? bindings.routes : bindings.actions) ?? {};
|
|
11718
|
-
const short = id.split("/").at(-1);
|
|
11719
|
-
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]))];
|
|
11720
|
-
if (tools.length > 0) return `${label} is unavailable: ${id}; this View offers it as ${tools.join(" and ")}, so call that tool`;
|
|
11721
|
-
const shown = offered.filter((entry) => named[entry.id] === void 0).map((entry) => entry.id);
|
|
11722
|
-
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`;
|
|
11723
|
-
const near = shown.filter((candidate) => candidate.split("/").at(-1) === short);
|
|
11724
|
-
if (near.length === 1) return `${label} is unavailable: ${id}; ids are exact as MNEMON VIEW ROUTES lists them: ${JSON.stringify(near[0])}`;
|
|
11725
|
-
const listed = [...near, ...shown.filter((candidate) => !near.includes(candidate))];
|
|
11726
|
-
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` : ""}`;
|
|
11727
|
-
}
|
|
11728
|
-
function memoryWakeBindings(generation, view) {
|
|
11729
|
-
const instances = generation.sourceInstances();
|
|
11730
|
-
const routes = {}, actions = {};
|
|
11731
|
-
for (const typeId of NAMED_SOURCE_TYPES) {
|
|
11732
|
-
const selected = namedToolInstance(instances.filter((source) => source.sourceTypeId === typeId), typeId);
|
|
11733
|
-
if (selected === void 0) continue;
|
|
11734
|
-
for (const route of view.routes.filter((route) => route.sourceInstanceKey === selected.sourceInstanceKey)) {
|
|
11735
|
-
if (view.readGrants.find((grant) => grant.id === route.readGrantId)?.schema !== `dsh-mnemon.${typeId}/v1`) continue;
|
|
11736
|
-
const tool = ROUTE_TOOLS[typeId]?.[route.sourceRouteId];
|
|
11737
|
-
if (tool !== void 0) routes[route.id] = tool;
|
|
11738
|
-
}
|
|
11739
|
-
for (const action of view.actionOffers.filter((action) => action.sourceInstanceKey === selected.sourceInstanceKey)) {
|
|
11740
|
-
const tool = ACTION_TOOLS[typeId]?.[action.sourceActionId];
|
|
11741
|
-
if (tool !== void 0) actions[action.id] = tool;
|
|
11742
|
-
}
|
|
11743
|
-
}
|
|
11744
|
-
return {
|
|
11745
|
-
routes,
|
|
11746
|
-
actions
|
|
11747
|
-
};
|
|
11748
|
-
}
|
|
11749
|
-
/** Explicitly pick display fields; never copy grants, authority, or opaque Source state. */
|
|
11750
|
-
function inspectMemoryView(generation, view, state, turn, memoryText) {
|
|
11751
|
-
return structuredClone({
|
|
11752
|
-
id: view.id,
|
|
11753
|
-
digest: view.digest,
|
|
11754
|
-
generationId: view.runtimeGeneration,
|
|
11755
|
-
createdAt: view.createdAt,
|
|
11756
|
-
state,
|
|
11757
|
-
...turn === void 0 ? {} : { turn },
|
|
11758
|
-
strategyTypeId: view.strategyTypeId,
|
|
11759
|
-
strategyInstanceKey: view.strategyInstanceKey,
|
|
11760
|
-
extensions: view.strategyExtensions ?? [],
|
|
11761
|
-
projection: view.projection.map(({ id, sourceInstanceKey, mode, text, revision }) => ({
|
|
11762
|
-
id,
|
|
11763
|
-
sourceInstanceKey,
|
|
11764
|
-
mode,
|
|
11765
|
-
text,
|
|
11766
|
-
revision
|
|
11767
|
-
})),
|
|
11768
|
-
sourcePresentations: view.sourcePresentations ?? [],
|
|
11769
|
-
routes: view.routes.map(({ id, sourceInstanceKey, sourceRouteId, description, maxCalls }) => ({
|
|
11770
|
-
id,
|
|
11771
|
-
sourceInstanceKey,
|
|
11772
|
-
operationId: sourceRouteId,
|
|
11773
|
-
description,
|
|
11774
|
-
maxCalls
|
|
11775
|
-
})),
|
|
11776
|
-
actions: view.actionOffers.map(({ id, sourceInstanceKey, sourceActionId, description }) => ({
|
|
11777
|
-
id,
|
|
11778
|
-
sourceInstanceKey,
|
|
11779
|
-
operationId: sourceActionId,
|
|
11780
|
-
description
|
|
11781
|
-
})),
|
|
11782
|
-
memoryText: memoryText ?? presentMemoryWake(generation, view).text,
|
|
11783
|
-
...view.guidance === void 0 ? {} : { guidance: view.guidance },
|
|
11784
|
-
diagnostics: (view.diagnostics ?? []).map((diagnostic) => diagnostic.message)
|
|
11785
|
-
});
|
|
11886
|
+
return typeof event.data.turn === "number" ? event.data.turn : void 0;
|
|
11786
11887
|
}
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11888
|
+
function resultCallId(event) {
|
|
11889
|
+
const message = event.data.message;
|
|
11890
|
+
return typeof message?.source?.callId === "string" && message.source.callId !== "" ? message.source.callId : void 0;
|
|
11891
|
+
}
|
|
11892
|
+
function emptyActivity(turn) {
|
|
11893
|
+
return {
|
|
11894
|
+
turn,
|
|
11895
|
+
count: 0,
|
|
11896
|
+
names: [],
|
|
11897
|
+
recalls: 0,
|
|
11898
|
+
writes: 0,
|
|
11899
|
+
documentSearches: 0,
|
|
11900
|
+
inspections: 0,
|
|
11901
|
+
failures: 0,
|
|
11902
|
+
retrieved: [],
|
|
11903
|
+
writebacks: []
|
|
11904
|
+
};
|
|
11798
11905
|
}
|
|
11799
11906
|
/**
|
|
11800
|
-
*
|
|
11801
|
-
*
|
|
11907
|
+
* Incremental durable-log projection. Repeated UI reads process only events
|
|
11908
|
+
* appended since the previous snapshot instead of rescanning the full session.
|
|
11802
11909
|
*/
|
|
11803
|
-
var
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
closed = false;
|
|
11814
|
-
lastInspection;
|
|
11815
|
-
lastWorkspace;
|
|
11816
|
-
releaseDelegation;
|
|
11817
|
-
constructor(agent, runtime, delegation) {
|
|
11818
|
-
this.agent = agent;
|
|
11819
|
-
this.runtime = runtime;
|
|
11820
|
-
this.delegation = delegation;
|
|
11821
|
-
if (delegation === void 0) return;
|
|
11822
|
-
this.releaseDelegation = runtime.executions.retain(agent, delegation);
|
|
11823
|
-
}
|
|
11824
|
-
get current() {
|
|
11825
|
-
return this.pinned;
|
|
11826
|
-
}
|
|
11827
|
-
/** Why the current turn has no View, when memory could not be composed for it. */
|
|
11828
|
-
get unavailableReason() {
|
|
11829
|
-
return this.unavailable?.reason;
|
|
11830
|
-
}
|
|
11831
|
-
inspect(workspaceRoot) {
|
|
11832
|
-
if (this.lastInspection === void 0) return void 0;
|
|
11833
|
-
if (workspaceRoot !== void 0 && resolve(workspaceRoot) !== this.lastWorkspace) return void 0;
|
|
11834
|
-
return structuredClone({
|
|
11835
|
-
...this.lastInspection,
|
|
11836
|
-
state: this.pinned === void 0 ? "recent" : "active"
|
|
11837
|
-
});
|
|
11838
|
-
}
|
|
11839
|
-
clearInspection() {
|
|
11840
|
-
this.lastInspection = void 0;
|
|
11841
|
-
this.lastWorkspace = void 0;
|
|
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();
|
|
11842
11920
|
}
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
if (this.
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
viewId: this.pinned.context.view.id
|
|
11850
|
-
};
|
|
11851
|
-
if (this.delegation !== void 0) return this.delegation;
|
|
11852
|
-
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;
|
|
11853
11927
|
return {
|
|
11854
|
-
|
|
11855
|
-
|
|
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
|
+
}))
|
|
11856
11941
|
};
|
|
11857
11942
|
}
|
|
11858
|
-
|
|
11859
|
-
if (
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
const generation = this.generation;
|
|
11865
|
-
const controller = new AbortController();
|
|
11866
|
-
const abort = () => controller.abort(signal.reason);
|
|
11867
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
11868
|
-
const result = this.pin(turn, generation, controller.signal).catch((error) => {
|
|
11869
|
-
if (!isMemoryCompositionUnavailable(error) || this.closed || this.generation !== generation) throw error;
|
|
11870
|
-
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, {
|
|
11871
11949
|
turn,
|
|
11872
|
-
|
|
11873
|
-
};
|
|
11874
|
-
|
|
11875
|
-
if (this.warned !== error.message) console.warn(`[dsh-mnemon] this turn runs without memory: ${error.message}`);
|
|
11876
|
-
this.warned = error.message;
|
|
11877
|
-
});
|
|
11878
|
-
this.pending = {
|
|
11879
|
-
turn,
|
|
11880
|
-
generation,
|
|
11881
|
-
controller,
|
|
11882
|
-
result
|
|
11883
|
-
};
|
|
11884
|
-
try {
|
|
11885
|
-
await result;
|
|
11886
|
-
} finally {
|
|
11887
|
-
signal?.removeEventListener("abort", abort);
|
|
11888
|
-
if (this.pending?.generation === generation) this.pending = void 0;
|
|
11950
|
+
name
|
|
11951
|
+
});
|
|
11952
|
+
return;
|
|
11889
11953
|
}
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
if (
|
|
11893
|
-
this.
|
|
11894
|
-
|
|
11895
|
-
this.pending
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
}
|
|
11901
|
-
dispose() {
|
|
11902
|
-
if (this.closed) return;
|
|
11903
|
-
this.closed = true;
|
|
11904
|
-
this.clearInspection();
|
|
11905
|
-
try {
|
|
11906
|
-
this.end();
|
|
11907
|
-
} finally {
|
|
11908
|
-
this.releaseDelegation?.();
|
|
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);
|
|
11909
11964
|
}
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
if (this.closed || this.generation !== generation) throw new Error("memory turn ended during View preparation");
|
|
11916
|
-
signal?.throwIfAborted();
|
|
11917
|
-
const inspection = inspectMemoryView(graph.memoryComposition.generation(context.view.runtimeGeneration), context.view, "active", turn, modelMemoryWake(graph, context).text);
|
|
11918
|
-
this.pinned = {
|
|
11919
|
-
turn,
|
|
11920
|
-
graph,
|
|
11921
|
-
context,
|
|
11922
|
-
release: execution.release
|
|
11923
|
-
};
|
|
11924
|
-
this.warned = void 0;
|
|
11925
|
-
this.lastInspection = inspection;
|
|
11926
|
-
this.lastWorkspace = context.scope.workspaceId === void 0 ? void 0 : resolve(context.scope.workspaceId);
|
|
11927
|
-
} catch (error) {
|
|
11928
|
-
execution.release();
|
|
11929
|
-
throw error;
|
|
11965
|
+
activity.count += 1;
|
|
11966
|
+
activity.names.push(call.name);
|
|
11967
|
+
if (event.data.error !== void 0) {
|
|
11968
|
+
activity.failures += 1;
|
|
11969
|
+
return;
|
|
11930
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;
|
|
11931
11976
|
}
|
|
11932
11977
|
};
|
|
11933
11978
|
//#endregion
|
|
@@ -12327,6 +12372,7 @@ var MnemonAgentLifecycle = class {
|
|
|
12327
12372
|
this.releaseView(payload.turn);
|
|
12328
12373
|
return decision;
|
|
12329
12374
|
}
|
|
12375
|
+
if (!this.disposed && openAgentTurn(this.agent) === payload.turn) await this.memoryTurn?.begin(payload.turn, payload.signal);
|
|
12330
12376
|
if (!this.config.lifecycleEnabled) return decision;
|
|
12331
12377
|
if (this.config.writeEnabled && this.config.writebackMode === "guided") this.recordTurnMessages(payload.turn, decision.messages);
|
|
12332
12378
|
if (payload.step !== 1) return decision;
|
|
@@ -12370,7 +12416,35 @@ var MnemonAgentLifecycle = class {
|
|
|
12370
12416
|
this.memoryTurn?.end(turn);
|
|
12371
12417
|
}
|
|
12372
12418
|
async finishTurn(payload) {
|
|
12373
|
-
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);
|
|
12374
12448
|
}
|
|
12375
12449
|
sessionEvent(session, event) {
|
|
12376
12450
|
if (session !== this.agent.session || event.type !== "turn/end") return;
|
|
@@ -12381,12 +12455,7 @@ var MnemonAgentLifecycle = class {
|
|
|
12381
12455
|
}
|
|
12382
12456
|
scheduleIdleReview(turn) {
|
|
12383
12457
|
this.cancelIdleReview(true);
|
|
12384
|
-
this.recordComposedTurn(turn);
|
|
12385
12458
|
if (!this.restoreWatermark() || !this.idleReviewAllowed()) return;
|
|
12386
|
-
const activity = this.ensureTurnActivity(turn);
|
|
12387
|
-
const tools = completedToolActivity(this.agent.session.snapshotEvents(), turn);
|
|
12388
|
-
activity.toolCallCount = tools.count;
|
|
12389
|
-
activity.toolNames = tools.names;
|
|
12390
12459
|
if (!this.reviewActivity().eligible || !this.reviewAdmitted(turn)) return;
|
|
12391
12460
|
const epoch = this.reviewEpoch;
|
|
12392
12461
|
const policy = this.reviewPolicy();
|
|
@@ -12506,11 +12575,8 @@ var MnemonAgentLifecycle = class {
|
|
|
12506
12575
|
this.reviewRunning = true;
|
|
12507
12576
|
this.reviewController = controller;
|
|
12508
12577
|
try {
|
|
12509
|
-
const available = await this.coordinator.checkReviewAccess(this.agent, controller.signal);
|
|
12510
12578
|
if (epoch !== this.reviewEpoch || policy !== this.reviewPolicy() || !this.idleReviewAllowed()) controller.abort(/* @__PURE__ */ new Error("Automatic idle review admission changed"));
|
|
12511
12579
|
controller.signal.throwIfAborted();
|
|
12512
|
-
this.reviewAccessBlocked = !available;
|
|
12513
|
-
if (!available) return;
|
|
12514
12580
|
if (this.config.idleReview.persistWatermark) try {
|
|
12515
12581
|
if (!this.restoreWatermark() || this.watermarkStore === void 0 || !this.idleReviewAllowed()) return;
|
|
12516
12582
|
const reserved = this.watermarkStore.reserve({
|
|
@@ -13315,7 +13381,21 @@ var MnemonLifecycle = class {
|
|
|
13315
13381
|
})));
|
|
13316
13382
|
stops.push(agent.ctx.on("agent/pre-step", (async (payload, next) => {
|
|
13317
13383
|
const decision = await next();
|
|
13318
|
-
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
|
+
}
|
|
13319
13399
|
return decision;
|
|
13320
13400
|
})));
|
|
13321
13401
|
stops.push(agent.ctx.on("session/event", ((session, event) => {
|
|
@@ -14136,8 +14216,6 @@ var TrajectoryWindow = class {
|
|
|
14136
14216
|
};
|
|
14137
14217
|
//#endregion
|
|
14138
14218
|
//#region src/host/skill-evolution.ts
|
|
14139
|
-
/** A new skill goes to the user's own DSH skills unless the page chose another root. */
|
|
14140
|
-
const DEFAULT_TARGET_ROOT = "user-dsh";
|
|
14141
14219
|
/** A conversation's turn bars read at most this many of its reviews. */
|
|
14142
14220
|
const ACTIVITY_REVIEWS = 50;
|
|
14143
14221
|
const WAITING_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -14159,6 +14237,7 @@ const BACKTEST_CONCURRENCY = 2;
|
|
|
14159
14237
|
*/
|
|
14160
14238
|
function retryingSource(source, attempts = 4) {
|
|
14161
14239
|
return {
|
|
14240
|
+
...source.scope === void 0 ? {} : { scope: source.scope },
|
|
14162
14241
|
read: (operation, input, signal) => source.read(operation, input, signal),
|
|
14163
14242
|
mutate: async (operation, input, signal) => {
|
|
14164
14243
|
for (let attempt = 1;; attempt++) try {
|
|
@@ -14172,6 +14251,35 @@ function retryingSource(source, attempts = 4) {
|
|
|
14172
14251
|
function message(error) {
|
|
14173
14252
|
return redact(error instanceof Error ? error.message : String(error));
|
|
14174
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
|
+
}
|
|
14175
14283
|
/**
|
|
14176
14284
|
* The Host half of skill evolution: it captures each conversation's
|
|
14177
14285
|
* trajectory as turns end, decides when a window has earned a review, runs
|
|
@@ -14351,7 +14459,8 @@ var SkillEvolution = class {
|
|
|
14351
14459
|
annotations: []
|
|
14352
14460
|
};
|
|
14353
14461
|
try {
|
|
14354
|
-
const
|
|
14462
|
+
const catalog = await source.read("catalog", null, controller.signal);
|
|
14463
|
+
const active = catalog.skills.filter((skill) => skill.shadowedBy === void 0);
|
|
14355
14464
|
const openSuggestions = [...await source.read("suggestions", { status: [
|
|
14356
14465
|
"pending",
|
|
14357
14466
|
"drafting",
|
|
@@ -14398,6 +14507,7 @@ var SkillEvolution = class {
|
|
|
14398
14507
|
expiryDays: current.expiryDays,
|
|
14399
14508
|
maxPendingPerSession: current.maxPendingPerSession
|
|
14400
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;
|
|
14401
14511
|
const result = await source.mutate("review-record", {
|
|
14402
14512
|
review: {
|
|
14403
14513
|
...base,
|
|
@@ -14407,7 +14517,10 @@ var SkillEvolution = class {
|
|
|
14407
14517
|
},
|
|
14408
14518
|
trajectory,
|
|
14409
14519
|
annotations: parsed.annotations,
|
|
14410
|
-
suggestions: parsed.suggestions
|
|
14520
|
+
suggestions: parsed.suggestions.map((suggestion) => suggestion.action === "create" && targetRoot !== void 0 ? {
|
|
14521
|
+
...suggestion,
|
|
14522
|
+
targetRoot
|
|
14523
|
+
} : suggestion),
|
|
14411
14524
|
cases: parsed.cases,
|
|
14412
14525
|
policy
|
|
14413
14526
|
});
|
|
@@ -14454,13 +14567,27 @@ var SkillEvolution = class {
|
|
|
14454
14567
|
*/
|
|
14455
14568
|
async draft(port, workspace, input) {
|
|
14456
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);
|
|
14457
14580
|
if (input.suggestionId !== void 0) await source.mutate("suggestion-status", {
|
|
14458
14581
|
id: input.suggestionId,
|
|
14459
14582
|
status: "drafting"
|
|
14460
14583
|
});
|
|
14461
14584
|
this.background("skill draft", async (signal) => {
|
|
14462
14585
|
try {
|
|
14463
|
-
await this.writeDraft(source, workspace, input,
|
|
14586
|
+
await this.writeDraft(source, workspace, input, {
|
|
14587
|
+
pack,
|
|
14588
|
+
targetRoot,
|
|
14589
|
+
guidance
|
|
14590
|
+
}, signal);
|
|
14464
14591
|
} catch (error) {
|
|
14465
14592
|
if (input.suggestionId !== void 0) await source.mutate("suggestion-status", {
|
|
14466
14593
|
id: input.suggestionId,
|
|
@@ -14475,14 +14602,9 @@ var SkillEvolution = class {
|
|
|
14475
14602
|
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId }
|
|
14476
14603
|
};
|
|
14477
14604
|
}
|
|
14478
|
-
async writeDraft(source, workspace, input, signal) {
|
|
14479
|
-
const pack = await source.read("evidence-pack", {
|
|
14480
|
-
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
|
|
14481
|
-
...input.skill === void 0 ? {} : { skill: input.skill }
|
|
14482
|
-
}, signal);
|
|
14605
|
+
async writeDraft(source, workspace, input, prepared, signal) {
|
|
14483
14606
|
const settings = this.host.settings();
|
|
14484
|
-
const
|
|
14485
|
-
const targetRoot = input.targetRoot ?? DEFAULT_TARGET_ROOT;
|
|
14607
|
+
const { pack, targetRoot, guidance } = prepared;
|
|
14486
14608
|
let prompt = skillDraftPrompt(pack, guidance, targetRoot);
|
|
14487
14609
|
let draft;
|
|
14488
14610
|
for (let attempt = 0; draft === void 0; attempt++) {
|
|
@@ -14908,10 +15030,16 @@ async function catalog(runtime, lifecycle) {
|
|
|
14908
15030
|
}
|
|
14909
15031
|
async function compositionStatus(runtime) {
|
|
14910
15032
|
const composition = runtime.graph.memoryComposition.inspect();
|
|
14911
|
-
const
|
|
15033
|
+
const strategy = runtime.graph.memoryComposition.current()?.strategy.definition.manifest;
|
|
14912
15034
|
return {
|
|
14913
15035
|
serving: composition.servingGenerationId !== void 0,
|
|
14914
|
-
...
|
|
15036
|
+
...strategy === void 0 ? {} : {
|
|
15037
|
+
strategyTypeId: strategy.typeId,
|
|
15038
|
+
workflows: {
|
|
15039
|
+
idleReview: strategy.typeId === mainStrategyTypeId(runtime.graph.config) && strategyDrives(strategy, "idle-review"),
|
|
15040
|
+
runtimeCapacity: strategyDrives(strategy, "runtime-capacity")
|
|
15041
|
+
}
|
|
15042
|
+
},
|
|
14915
15043
|
evaluation: composition.evaluation,
|
|
14916
15044
|
sources: (await catalog(runtime)).sources,
|
|
14917
15045
|
configuration: runtime.graph.config.memoryTopology
|