dsh-mnemon 0.5.7 → 0.5.9
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 +547 -25
- package/lib/client.js +22 -2
- package/lib/index.js +346 -127
- package/lib/types/src/client/locales.d.ts +6 -0
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { strToU8, unzipSync, zipSync } from "fflate";
|
|
|
13
13
|
import { DOCUMENTS_ACTIVE_LIMIT_BYTES, DOCUMENTS_VERSION } from "dsh-mnemon-source-documents/contracts";
|
|
14
14
|
import { RUNTIME_ENTRY_DELIMITER, RUNTIME_MEMORY_LIMITS, RUNTIME_MEMORY_VERSION } from "dsh-mnemon-source-runtime/contracts";
|
|
15
15
|
import { threeTierActionWorkflow } from "dsh-mnemon-strategy-default-three-tier/extension-sdk";
|
|
16
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
16
17
|
import { fileURLToPath } from "node:url";
|
|
17
18
|
import { findMnemonCommand, mnemonNpmLauncher, nodeLauncherEnvironment } from "dsh-mnemon-source-memory-spaces/native-cli";
|
|
18
19
|
import { spawn } from "node:child_process";
|
|
@@ -2611,6 +2612,86 @@ function hostSessionEventAt(session, seq) {
|
|
|
2611
2612
|
return hostSessionEvents(session)[seq];
|
|
2612
2613
|
}
|
|
2613
2614
|
//#endregion
|
|
2615
|
+
//#region src/host/review-tools.ts
|
|
2616
|
+
const startingReview = new AsyncLocalStorage();
|
|
2617
|
+
/**
|
|
2618
|
+
* DSH restrict() filters inherited capabilities, leaving own-scope plugin tools
|
|
2619
|
+
* visible. Attach its monotonic execution guard during publication, before a
|
|
2620
|
+
* review child can run. Async context attributes concurrent provider starts;
|
|
2621
|
+
* public registry ownership verifies the exact causal parent.
|
|
2622
|
+
*/
|
|
2623
|
+
async function startGuardedReview(host, parent, toolNames, start) {
|
|
2624
|
+
const agents = host.agents;
|
|
2625
|
+
if (typeof agents?.isOwnedBy !== "function") throw new Error("Mnemon review requires DSH Agent ownership and scoped tool guard support");
|
|
2626
|
+
const pending = Symbol("Mnemon review publication");
|
|
2627
|
+
const allowed = new Set(toolNames);
|
|
2628
|
+
const guards = /* @__PURE__ */ new Map();
|
|
2629
|
+
let attachmentError;
|
|
2630
|
+
let run;
|
|
2631
|
+
const listener = host.on("agent/created", (({ agent }) => {
|
|
2632
|
+
if (startingReview.getStore() !== pending || !agents.isOwnedBy(agent.id, parent)) return;
|
|
2633
|
+
try {
|
|
2634
|
+
const tools = agent.ctx.tools;
|
|
2635
|
+
if (typeof tools?.guard !== "function") throw new Error("Mnemon review requires DSH scoped tool guard support");
|
|
2636
|
+
const dispose = tools.guard((execution) => {
|
|
2637
|
+
if (execution.name === "run_code" || execution.name !== void 0 && allowed.has(execution.name)) return;
|
|
2638
|
+
return `Mnemon review cannot execute ${JSON.stringify(execution.name)}; reuse the inherited checkpoint and bounded Document search.`;
|
|
2639
|
+
});
|
|
2640
|
+
if (typeof dispose !== "function") throw new Error("Mnemon review tool guard did not return a disposer");
|
|
2641
|
+
guards.set(agent, dispose);
|
|
2642
|
+
} catch (error) {
|
|
2643
|
+
attachmentError = error;
|
|
2644
|
+
throw error;
|
|
2645
|
+
}
|
|
2646
|
+
}));
|
|
2647
|
+
if (typeof listener !== "function") throw new Error("Mnemon review creation observer did not return a disposer");
|
|
2648
|
+
let disposeObserver = listener;
|
|
2649
|
+
const closeObserver = async () => {
|
|
2650
|
+
const dispose = disposeObserver;
|
|
2651
|
+
disposeObserver = void 0;
|
|
2652
|
+
await dispose?.();
|
|
2653
|
+
};
|
|
2654
|
+
const release = async () => {
|
|
2655
|
+
const disposers = [...guards.values()];
|
|
2656
|
+
guards.clear();
|
|
2657
|
+
const failed = (await Promise.allSettled(disposers.map(async (dispose) => {
|
|
2658
|
+
await dispose();
|
|
2659
|
+
}))).filter((outcome) => outcome.status === "rejected");
|
|
2660
|
+
if (failed.length) throw new AggregateError(failed.map((outcome) => outcome.reason), "Mnemon review guard cleanup failed");
|
|
2661
|
+
};
|
|
2662
|
+
try {
|
|
2663
|
+
run = await startingReview.run(pending, start);
|
|
2664
|
+
if (attachmentError !== void 0) throw attachmentError;
|
|
2665
|
+
const child = run.localAgent;
|
|
2666
|
+
if (child === void 0 || !guards.has(child)) throw new Error("Mnemon review provider did not publish a local child with its scoped tool guard");
|
|
2667
|
+
await closeObserver();
|
|
2668
|
+
const active = run;
|
|
2669
|
+
return {
|
|
2670
|
+
id: active.id,
|
|
2671
|
+
localAgent: child,
|
|
2672
|
+
result: active.result,
|
|
2673
|
+
async dispose() {
|
|
2674
|
+
try {
|
|
2675
|
+
await active.dispose();
|
|
2676
|
+
} finally {
|
|
2677
|
+
await release();
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
try {
|
|
2683
|
+
await closeObserver();
|
|
2684
|
+
} catch {}
|
|
2685
|
+
try {
|
|
2686
|
+
await run?.dispose();
|
|
2687
|
+
} catch {}
|
|
2688
|
+
try {
|
|
2689
|
+
await release();
|
|
2690
|
+
} catch {}
|
|
2691
|
+
throw error;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
//#endregion
|
|
2614
2695
|
//#region src/host/subagent.ts
|
|
2615
2696
|
function evidenceInsights(evidence) {
|
|
2616
2697
|
return evidence.items.map((item) => {
|
|
@@ -2643,7 +2724,22 @@ const REVIEW_TOOLS = [
|
|
|
2643
2724
|
"mnemon_runtime_memory",
|
|
2644
2725
|
"mnemon_document_create"
|
|
2645
2726
|
];
|
|
2646
|
-
const
|
|
2727
|
+
const RESULT_TOOL_NAME = "mnemon_subagent_result";
|
|
2728
|
+
const RESULT_TOOL_INPUT_SCHEMA = {
|
|
2729
|
+
type: "object",
|
|
2730
|
+
properties: {
|
|
2731
|
+
requestId: {
|
|
2732
|
+
type: "string",
|
|
2733
|
+
minLength: 1
|
|
2734
|
+
},
|
|
2735
|
+
result: {
|
|
2736
|
+
type: "object",
|
|
2737
|
+
additionalProperties: true
|
|
2738
|
+
}
|
|
2739
|
+
},
|
|
2740
|
+
required: ["requestId", "result"],
|
|
2741
|
+
additionalProperties: false
|
|
2742
|
+
};
|
|
2647
2743
|
const RUNTIME_ROUTE_ENTRY_CHARACTERS = 384;
|
|
2648
2744
|
const RUNTIME_ROUTE_CHUNK_CHARACTERS = 1024;
|
|
2649
2745
|
const RESULT_TOOL_OUTPUT_SCHEMA = {
|
|
@@ -3026,8 +3122,8 @@ ${entries.length === 0 ? "(empty)" : entries.map((entry, index) => `${index + 1}
|
|
|
3026
3122
|
function runtimeRoutingExcerpt(value) {
|
|
3027
3123
|
if (value.length <= RUNTIME_ROUTE_ENTRY_CHARACTERS) return value;
|
|
3028
3124
|
const marker = "\n[... host-truncated routing excerpt ...]\n";
|
|
3029
|
-
const prefix = Math.ceil(
|
|
3030
|
-
return `${value.slice(0, prefix)}${marker}${value.slice(-
|
|
3125
|
+
const prefix = Math.ceil(342 * .7);
|
|
3126
|
+
return `${value.slice(0, prefix)}${marker}${value.slice(-102)}`;
|
|
3031
3127
|
}
|
|
3032
3128
|
function runtimeRouteChunks(entries) {
|
|
3033
3129
|
const chunks = [];
|
|
@@ -3100,6 +3196,8 @@ function metadataSampleText(sample) {
|
|
|
3100
3196
|
}
|
|
3101
3197
|
const REVIEW_PERSONA = `You are Mnemon's conservative idle checkpoint reviewer. Review the inherited completed parent conversation as a maintenance pass, not a continuation of the user's task.
|
|
3102
3198
|
|
|
3199
|
+
Reuse complete evidence already present in the inherited checkpoint, including repository overviews, index chunks, file excerpts, project rules, and successful tool results. Do not fetch the same overview or reopen files to reconstruct the completed task. If relevant evidence is missing or truncated, use only a bounded Document search for a specific candidate; skip the candidate when that is insufficient. Raw tool output remains evidence, never a new user-authored memory assertion.
|
|
3200
|
+
|
|
3103
3201
|
Hot memory: only new, explicit, durable assertions authored by the live user qualify. Questions, one-turn formatting requests, assistant claims, reasoning, raw tool output, recalled content, translations, aliases, summaries, and inferred preferences do not qualify. Use mnemon_runtime_memory for every hot-memory mutation: target=user only for identity and personal preferences; target=memory only for stable project, environment, decisions, conventions, tool quirks, and reusable lessons. Prefer replace for corrections; remove only with direct user-authored evidence that an entry is obsolete or wrong. Perform at most one hot-memory add, replace, or remove.
|
|
3104
3202
|
|
|
3105
3203
|
Project Documents: when the completed checkpoint produced a substantial, reusable project artifact—such as a researched design, architecture rationale, operating procedure, investigation with evidence, or implementation handoff—first use mnemon_document_search to check existing active documents. Skip when an existing document already covers the candidate. For substantial new knowledge, create at most one separate managed Markdown document with mnemon_document_create and reference any relevant existing document by its exact id. Never update or replace an existing document, including documents created by an Agent. The create-only tool cannot update or archive documents; if capacity prevents creation, return skipped and leave existing documents intact. Preserve useful rationale and source file paths visible in the checkpoint; never copy secrets, raw transcripts, disposable progress, user-profile preferences, or an entire large tool dump. Simple chats and routine edits need no document.
|
|
@@ -3264,6 +3362,9 @@ var MnemonSubagentCoordinator = class {
|
|
|
3264
3362
|
runtimeQueue = Promise.resolve();
|
|
3265
3363
|
documentQueue = Promise.resolve();
|
|
3266
3364
|
observedReads = /* @__PURE__ */ new WeakMap();
|
|
3365
|
+
resultRequests = /* @__PURE__ */ new Map();
|
|
3366
|
+
disposeResultTool;
|
|
3367
|
+
disposed = false;
|
|
3267
3368
|
constructor(subagents, runtimeSource, resultRuntime, taskAgentModelResolver, runtimeMaintenanceMaxTokensResolver, runtimeMaintenanceTaskRunner) {
|
|
3268
3369
|
this.subagents = subagents;
|
|
3269
3370
|
this.runtimeSource = runtimeSource;
|
|
@@ -3271,6 +3372,37 @@ var MnemonSubagentCoordinator = class {
|
|
|
3271
3372
|
this.taskAgentModelResolver = taskAgentModelResolver;
|
|
3272
3373
|
this.runtimeMaintenanceMaxTokensResolver = runtimeMaintenanceMaxTokensResolver;
|
|
3273
3374
|
this.runtimeMaintenanceTaskRunner = runtimeMaintenanceTaskRunner;
|
|
3375
|
+
if (resultRuntime === void 0) return;
|
|
3376
|
+
const registration = resultRuntime.tools.register({
|
|
3377
|
+
name: RESULT_TOOL_NAME,
|
|
3378
|
+
description: "Record a Mnemon delegated result. The child must use its current requestId and result schema from its completion instructions.",
|
|
3379
|
+
parameters: RESULT_TOOL_INPUT_SCHEMA,
|
|
3380
|
+
output: {
|
|
3381
|
+
schema: RESULT_TOOL_OUTPUT_SCHEMA,
|
|
3382
|
+
render: () => [{
|
|
3383
|
+
type: "text",
|
|
3384
|
+
text: "Mnemon subagent result recorded."
|
|
3385
|
+
}]
|
|
3386
|
+
},
|
|
3387
|
+
execute: async (args, execution) => {
|
|
3388
|
+
if (!isSubagent(execution.agent)) throw new Error("Mnemon subagent result tools are restricted to delegated children");
|
|
3389
|
+
execution.signal.throwIfAborted();
|
|
3390
|
+
assertDshOutputValue(RESULT_TOOL_INPUT_SCHEMA, args);
|
|
3391
|
+
const request = object$4(args);
|
|
3392
|
+
const submit = this.resultRequests.get(String(request.requestId));
|
|
3393
|
+
if (this.disposed || submit === void 0) throw new Error("Mnemon subagent result request is unknown or no longer active");
|
|
3394
|
+
return submit(request.result, execution);
|
|
3395
|
+
}
|
|
3396
|
+
});
|
|
3397
|
+
if (typeof registration !== "function") throw new Error("dsh-mnemon subagent result tool registration did not return a disposer");
|
|
3398
|
+
this.disposeResultTool = registration;
|
|
3399
|
+
}
|
|
3400
|
+
dispose() {
|
|
3401
|
+
this.disposed = true;
|
|
3402
|
+
this.resultRequests.clear();
|
|
3403
|
+
const dispose = this.disposeResultTool;
|
|
3404
|
+
this.disposeResultTool = void 0;
|
|
3405
|
+
return dispose?.();
|
|
3274
3406
|
}
|
|
3275
3407
|
snapshot() {
|
|
3276
3408
|
return { ...this.counters };
|
|
@@ -3457,19 +3589,29 @@ var MnemonSubagentCoordinator = class {
|
|
|
3457
3589
|
}
|
|
3458
3590
|
const graph = this.runtimeSource.forAgent(parent);
|
|
3459
3591
|
const scope = agentScope(parent, graph.config);
|
|
3460
|
-
const
|
|
3461
|
-
|
|
3462
|
-
runtime,
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3592
|
+
const lease = graph.memoryComposition.acquire();
|
|
3593
|
+
try {
|
|
3594
|
+
const runtime = graph.source("runtime", scope).forGeneration(lease.generation);
|
|
3595
|
+
const context = {
|
|
3596
|
+
runtime,
|
|
3597
|
+
inspectRuntime: runtime,
|
|
3598
|
+
maintain: threeTierActionWorkflow(graph.config.memoryTopology.strategyId, "runtime", "mutate") !== void 0,
|
|
3599
|
+
commit: () => runtime.mutate("mutate", request, signal),
|
|
3600
|
+
memorySpaces: async () => {
|
|
3601
|
+
if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
|
|
3602
|
+
assertParticipation(graph.config, "memory-spaces", "write", "automatic");
|
|
3603
|
+
const source = graph.source("memory-spaces", scope).forGeneration(lease.generation);
|
|
3604
|
+
return {
|
|
3605
|
+
source,
|
|
3606
|
+
cleanup: source
|
|
3607
|
+
};
|
|
3608
|
+
},
|
|
3609
|
+
model: (...args) => this.runtimeModel(scope, parent, signal, ...args)
|
|
3610
|
+
};
|
|
3611
|
+
return await this.enqueueRuntime(context, request, signal);
|
|
3612
|
+
} finally {
|
|
3613
|
+
lease.release();
|
|
3614
|
+
}
|
|
3473
3615
|
}
|
|
3474
3616
|
enqueueRuntime(context, request, signal) {
|
|
3475
3617
|
const operation = this.runtimeQueue.then(() => this.runtimeLocked(context, request, signal));
|
|
@@ -3503,6 +3645,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
3503
3645
|
const runtime = graph.source(source.sourceTypeId, turn.scope).forInstance(source.sourceInstanceKey).forTurn(turn).forGeneration(lease.generation);
|
|
3504
3646
|
const result = await this.enqueueRuntime({
|
|
3505
3647
|
runtime,
|
|
3648
|
+
inspectRuntime: graph.source("runtime", turn.scope).forInstance(source.sourceInstanceKey).forGeneration(lease.generation),
|
|
3506
3649
|
maintain: true,
|
|
3507
3650
|
commit,
|
|
3508
3651
|
assertWritable: () => {
|
|
@@ -3537,6 +3680,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
3537
3680
|
let view;
|
|
3538
3681
|
const result = await this.enqueueRuntime({
|
|
3539
3682
|
runtime,
|
|
3683
|
+
inspectRuntime: runtime,
|
|
3540
3684
|
maintain: true,
|
|
3541
3685
|
...request.expectedRevision === void 0 ? {} : { expectedRevision: request.expectedRevision },
|
|
3542
3686
|
assertWritable: () => {
|
|
@@ -3575,13 +3719,19 @@ var MnemonSubagentCoordinator = class {
|
|
|
3575
3719
|
const candidates = generation.sourceInstances().filter((source) => source.sourceTypeId === "memory-spaces" && view.actionOffers.some((offer) => offer.sourceInstanceKey === source.sourceInstanceKey && offer.sourceActionId === "remember" && offer.authority === void 0));
|
|
3576
3720
|
if (candidates.length === 0) throw new Error("Source Action is not offered by the current View: memory-spaces/remember");
|
|
3577
3721
|
if (candidates.length !== 1) throw new Error("Runtime archival requires one unambiguous writable Memory Spaces Source");
|
|
3578
|
-
|
|
3722
|
+
const cleanup = graph.source("memory-spaces", scope).forInstance(candidates[0].sourceInstanceKey).forGeneration(generation);
|
|
3723
|
+
let source = cleanup;
|
|
3579
3724
|
if (turn !== void 0) source = source.forTurn(turn);
|
|
3580
3725
|
const grant = view.readGrants.find((grant) => grant.sourceInstanceKey === candidates[0].sourceInstanceKey && grant.schema === "dsh-mnemon.memory-spaces/v1");
|
|
3581
3726
|
if (grant === void 0) throw new Error("Runtime archival requires the selected Memory Spaces namespace scope");
|
|
3582
3727
|
return {
|
|
3583
3728
|
source,
|
|
3584
|
-
|
|
3729
|
+
cleanup,
|
|
3730
|
+
memoryBodyIds: new Set(strings(object$4(grant.value).memoryBodyIds)),
|
|
3731
|
+
writeScope: {
|
|
3732
|
+
viewId: view.id,
|
|
3733
|
+
grant
|
|
3734
|
+
}
|
|
3585
3735
|
};
|
|
3586
3736
|
}
|
|
3587
3737
|
runtimeModel(scope, parent, signal, operation, label, prompt, schema, persona) {
|
|
@@ -3827,8 +3977,28 @@ ${naturalRequest(request)}`;
|
|
|
3827
3977
|
if (request.target === "user") return this.compactUserAndCommit(context, request, plan, signal);
|
|
3828
3978
|
const archive = await context.memorySpaces();
|
|
3829
3979
|
const memoryService = archive.source;
|
|
3830
|
-
|
|
3831
|
-
|
|
3980
|
+
let allowed = archive.memoryBodyIds;
|
|
3981
|
+
const readDirectory = async () => {
|
|
3982
|
+
const catalog = await memoryService.read("body-directory", archive.writeScope === void 0 ? null : { writeScope: archive.writeScope }, signal);
|
|
3983
|
+
allowed = archive.memoryBodyIds;
|
|
3984
|
+
if (catalog.writeScope !== void 0) {
|
|
3985
|
+
const scope = catalog.writeScope;
|
|
3986
|
+
if (archive.writeScope === void 0 || scope === null || scope.viewId !== archive.writeScope.viewId || scope.sourceInstanceKey !== archive.writeScope.grant.sourceInstanceKey || !Array.isArray(scope.memoryBodyIds) || scope.memoryBodyIds.length > 1e4 || scope.memoryBodyIds.some((id) => typeof id !== "string" || id.trim() === "" || id !== id.trim())) throw new Error("Runtime archival received an invalid Memory Spaces write scope; pending write was not committed; retry the same request in a new turn");
|
|
3987
|
+
allowed = new Set(scope.memoryBodyIds);
|
|
3988
|
+
}
|
|
3989
|
+
return catalog;
|
|
3990
|
+
};
|
|
3991
|
+
const catalog = await readDirectory();
|
|
3992
|
+
const writable = (body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember === true && (allowed === void 0 || allowed.has(body.id));
|
|
3993
|
+
const eligible = (body) => writable(body) && body.provider.capabilities.writeMode === "exact" && body.provider.capabilities.forget === true;
|
|
3994
|
+
const eligibleBodies = catalog.items.filter(eligible);
|
|
3995
|
+
if (eligibleBodies.length === 0) {
|
|
3996
|
+
const writableBodies = catalog.items.filter(writable);
|
|
3997
|
+
const unsupported = writableBodies.map((body) => `${body.id} (provider=${body.provider.id}, writeMode=${body.provider.capabilities.writeMode}, forget=${body.provider.capabilities.forget})`).join(", ");
|
|
3998
|
+
const reason = catalog.items.length === 0 ? "Memory Space body-directory is empty" : allowed?.size === 0 ? "current View write scope is empty" : allowed !== void 0 && catalog.items.some((body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember && !allowed?.has(body.id)) && writableBodies.length === 0 ? "current View write scope excludes the active writable Memory Spaces" : writableBodies.length === 0 ? "no active writable Memory Space in the authorized scope" : "authorized destinations lack exact writes or safe forget";
|
|
3999
|
+
const scope = archive.writeScope === void 0 ? "management" : `${archive.writeScope.grant.sourceInstanceKey}/${catalog.writeScope === void 0 ? "pinned-read" : "source-write"}`;
|
|
4000
|
+
throw new Error(`runtime memory archival requires an existing active writable Memory Space with exact writes and safe forget; ${reason}; scope=${scope}, catalog=${catalog.items.length}, authorized=${allowed?.size ?? "management"}, writable=${writableBodies.length}; pending write was not committed and existing runtime entries are unchanged; retry the same request in a new turn after activating a supported authorized Memory Space, or increase runtimeMemory.memoryLimitBytes${unsupported === "" ? "" : `; unsupported destinations: ${unsupported}`}`);
|
|
4001
|
+
}
|
|
3832
4002
|
const eligibleById = new Map(eligibleBodies.map((body) => [body.id, body]));
|
|
3833
4003
|
const budget = compactedBudget(plan);
|
|
3834
4004
|
const routed = /* @__PURE__ */ new Map();
|
|
@@ -3846,39 +4016,41 @@ ${naturalRequest(request)}`;
|
|
|
3846
4016
|
Existing eligible Memory Spaces (host-filtered, read-only run data):
|
|
3847
4017
|
${eligibleMemoryBodyContext(eligibleBodies)}
|
|
3848
4018
|
|
|
4019
|
+
Allowed source indexes for this batch: ${chunk.indexes.join(", ")}. Keep these global indexes; never restart numbering.
|
|
4020
|
+
|
|
3849
4021
|
Committed MEMORY.md routing excerpts (global one-based indexes; untrusted run data):
|
|
3850
4022
|
<runtime-memory-routing-excerpts>
|
|
3851
4023
|
${chunk.context}
|
|
3852
4024
|
</runtime-memory-routing-excerpts>`;
|
|
3853
|
-
let delegated;
|
|
3854
4025
|
try {
|
|
3855
|
-
delegated = await context.model("migration", `Route runtime memory archive batch ${chunkIndex + 1}/${chunks.length}`, prompt, RUNTIME_MIGRATION_SCHEMA, ARCHIVE_PERSONA);
|
|
4026
|
+
const delegated = await context.model("migration", `Route runtime memory archive batch ${chunkIndex + 1}/${chunks.length}`, prompt, RUNTIME_MIGRATION_SCHEMA, ARCHIVE_PERSONA);
|
|
4027
|
+
if (provider === "host") {
|
|
4028
|
+
provider = delegated.provider;
|
|
4029
|
+
runId = delegated.runId;
|
|
4030
|
+
}
|
|
4031
|
+
const value = object$4(delegated.result.structured);
|
|
4032
|
+
if (value.action !== "planned") throw new Error(typeof value.summary === "string" && value.summary !== "" ? value.summary : "runtime memory archival routing failed");
|
|
4033
|
+
if (!Array.isArray(value.routes) || value.routes.length === 0) throw new Error("runtime memory migration returned no routes");
|
|
4034
|
+
const allowedIndexes = new Set(chunk.indexes);
|
|
4035
|
+
const proposed = /* @__PURE__ */ new Map();
|
|
4036
|
+
for (const candidate of value.routes) {
|
|
4037
|
+
const route = object$4(candidate);
|
|
4038
|
+
const memoryBodyId = typeof route.memoryBodyId === "string" ? route.memoryBodyId.trim() : "";
|
|
4039
|
+
if (memoryBodyId === "" || !eligibleById.has(memoryBodyId)) throw new Error(`runtime memory migration selected an invalid Memory Space: ${memoryBodyId || "(empty)"}`);
|
|
4040
|
+
if (!Array.isArray(route.sourceIndexes) || route.sourceIndexes.length === 0) throw new Error("runtime memory migration route must contain source indexes");
|
|
4041
|
+
for (const sourceIndex of route.sourceIndexes) {
|
|
4042
|
+
if (!Number.isInteger(sourceIndex) || !allowedIndexes.has(sourceIndex) || proposed.has(sourceIndex)) throw new Error("runtime memory migration route coverage is invalid");
|
|
4043
|
+
proposed.set(sourceIndex, memoryBodyId);
|
|
4044
|
+
}
|
|
4045
|
+
}
|
|
4046
|
+
if (chunk.indexes.some((index) => !proposed.has(index))) throw new Error("runtime memory migration omitted committed archive sources");
|
|
4047
|
+
for (const [index, memoryBodyId] of proposed) routed.set(index, memoryBodyId);
|
|
4048
|
+
if (typeof value.summary === "string" && value.summary.trim() !== "") summaries.push(value.summary.trim());
|
|
3856
4049
|
} catch (error) {
|
|
3857
4050
|
signal.throwIfAborted();
|
|
3858
4051
|
for (const index of chunk.indexes) routed.set(index, fallbackBody.id);
|
|
3859
|
-
summaries.push(`batch ${chunkIndex + 1} routed to ${fallbackBody.id} deterministically (routing
|
|
3860
|
-
continue;
|
|
3861
|
-
}
|
|
3862
|
-
if (provider === "host") {
|
|
3863
|
-
provider = delegated.provider;
|
|
3864
|
-
runId = delegated.runId;
|
|
3865
|
-
}
|
|
3866
|
-
const value = object$4(delegated.result.structured);
|
|
3867
|
-
if (value.action !== "planned") throw new Error(typeof value.summary === "string" && value.summary !== "" ? value.summary : "runtime memory archival routing failed");
|
|
3868
|
-
if (!Array.isArray(value.routes) || value.routes.length === 0) throw new Error("runtime memory migration returned no routes");
|
|
3869
|
-
const allowedIndexes = new Set(chunk.indexes);
|
|
3870
|
-
for (const candidate of value.routes) {
|
|
3871
|
-
const route = object$4(candidate);
|
|
3872
|
-
const memoryBodyId = typeof route.memoryBodyId === "string" ? route.memoryBodyId.trim() : "";
|
|
3873
|
-
if (memoryBodyId === "" || !eligibleById.has(memoryBodyId)) throw new Error(`runtime memory migration selected an invalid Memory Space: ${memoryBodyId || "(empty)"}`);
|
|
3874
|
-
if (!Array.isArray(route.sourceIndexes) || route.sourceIndexes.length === 0) throw new Error("runtime memory migration route must contain source indexes");
|
|
3875
|
-
for (const sourceIndex of route.sourceIndexes) {
|
|
3876
|
-
if (!Number.isInteger(sourceIndex) || !allowedIndexes.has(sourceIndex) || routed.has(sourceIndex)) throw new Error("runtime memory migration route coverage is invalid");
|
|
3877
|
-
routed.set(sourceIndex, memoryBodyId);
|
|
3878
|
-
}
|
|
4052
|
+
summaries.push(`batch ${chunkIndex + 1} routed to ${fallbackBody.id} deterministically (routing failed: ${safeFailureDetail(error instanceof Error ? error.message : String(error))})`);
|
|
3879
4053
|
}
|
|
3880
|
-
if (chunk.indexes.some((index) => !routed.has(index))) throw new Error("runtime memory migration omitted committed archive sources");
|
|
3881
|
-
if (typeof value.summary === "string" && value.summary.trim() !== "") summaries.push(value.summary.trim());
|
|
3882
4054
|
}
|
|
3883
4055
|
summary = summaries.join(" ");
|
|
3884
4056
|
}
|
|
@@ -3889,10 +4061,12 @@ ${chunk.context}
|
|
|
3889
4061
|
...branches === void 0 ? {} : { branches }
|
|
3890
4062
|
}));
|
|
3891
4063
|
if ((await runtimeMemory.read("maintenance-plan", request, signal)).revision !== plan.revision) throw new Error("runtime memory changed while archival was running; no archive writes were attempted");
|
|
4064
|
+
const destinations = await readDirectory();
|
|
4065
|
+
for (const memoryBodyId of new Set(routed.values())) if (!destinations.items.some((body) => body.id === memoryBodyId && eligible(body))) throw new Error(`runtime archive Memory Space ${memoryBodyId} is no longer eligible; no archive writes were attempted`);
|
|
3892
4066
|
signal.throwIfAborted();
|
|
3893
4067
|
context.assertWritable?.();
|
|
3894
4068
|
const sources = runtimeMigrationSources(plan.revision, plan.entries);
|
|
3895
|
-
const
|
|
4069
|
+
const requests = sources.map((source) => {
|
|
3896
4070
|
const entry = plan.entries[source.index - 1];
|
|
3897
4071
|
return {
|
|
3898
4072
|
content: entry.content,
|
|
@@ -3902,50 +4076,102 @@ ${chunk.context}
|
|
|
3902
4076
|
memoryBodyId: routed.get(source.index),
|
|
3903
4077
|
...entry.branches === void 0 || entry.branches.length === 0 ? {} : { tags: entry.branches.map((branch) => `branch:${branch}`) }
|
|
3904
4078
|
};
|
|
3905
|
-
})
|
|
3906
|
-
|
|
3907
|
-
const
|
|
3908
|
-
const
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
const
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
source
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
revision: mutation.revision,
|
|
3941
|
-
maintenance: {
|
|
3942
|
-
kind: "mnemon-archive",
|
|
3943
|
-
runId,
|
|
3944
|
-
provider,
|
|
3945
|
-
summary,
|
|
3946
|
-
memoryBodyIds: [...memoryBodyIds]
|
|
4079
|
+
});
|
|
4080
|
+
const archiveResults = /* @__PURE__ */ new Map();
|
|
4081
|
+
const verified = /* @__PURE__ */ new Map();
|
|
4082
|
+
const created = /* @__PURE__ */ new Map();
|
|
4083
|
+
let commitAttempted = false;
|
|
4084
|
+
try {
|
|
4085
|
+
for (const memoryBodyId of new Set(routed.values())) {
|
|
4086
|
+
signal.throwIfAborted();
|
|
4087
|
+
context.assertWritable?.();
|
|
4088
|
+
if (archive.writeScope !== void 0 && !(await readDirectory()).items.some((body) => body.id === memoryBodyId && eligible(body))) throw new Error(`runtime archive Memory Space ${memoryBodyId} is no longer authorized or eligible; pending write was not committed`);
|
|
4089
|
+
const batch = sources.filter((source) => routed.get(source.index) === memoryBodyId);
|
|
4090
|
+
const written = await memoryService.mutate("remember-many", { requests: batch.map((source) => requests[source.index - 1]) }, signal);
|
|
4091
|
+
for (const [offset, result] of written.entries()) {
|
|
4092
|
+
const source = batch[offset];
|
|
4093
|
+
if (source === void 0) continue;
|
|
4094
|
+
const receipt = optionalObject(result);
|
|
4095
|
+
const states = mutationStates(result);
|
|
4096
|
+
const id = typeof receipt?.id === "string" && receipt.id.trim() !== "" ? receipt.id : void 0;
|
|
4097
|
+
if (mutationResultCommitted(result) && states.some((state) => [
|
|
4098
|
+
"added",
|
|
4099
|
+
"created",
|
|
4100
|
+
"stored"
|
|
4101
|
+
].includes(state)) && !states.some((state) => [
|
|
4102
|
+
"updated",
|
|
4103
|
+
"replaced",
|
|
4104
|
+
"merged",
|
|
4105
|
+
"skipped"
|
|
4106
|
+
].includes(state)) && id !== void 0 && (receipt?.memoryBodyId === void 0 || receipt.memoryBodyId === memoryBodyId)) created.set(JSON.stringify([memoryBodyId, id]), {
|
|
4107
|
+
id,
|
|
4108
|
+
memoryBodyId
|
|
4109
|
+
});
|
|
4110
|
+
archiveResults.set(source.index, result);
|
|
4111
|
+
}
|
|
4112
|
+
if (written.length !== batch.length) throw new Error("runtime archive batch did not return one receipt per source entry");
|
|
4113
|
+
for (const source of batch) verified.set(source.index, await this.archiveRuntimeEntry(memoryService, memoryBodyId, plan.entries[source.index - 1], archiveResults.get(source.index), signal));
|
|
3947
4114
|
}
|
|
3948
|
-
|
|
4115
|
+
const lineage = [];
|
|
4116
|
+
const memoryBodyIds = /* @__PURE__ */ new Set();
|
|
4117
|
+
for (const source of sources) {
|
|
4118
|
+
const memoryBodyId = routed.get(source.index);
|
|
4119
|
+
const destination = verified.get(source.index);
|
|
4120
|
+
memoryBodyIds.add(memoryBodyId);
|
|
4121
|
+
lineage.push({
|
|
4122
|
+
source: {
|
|
4123
|
+
layerId: source.layerId,
|
|
4124
|
+
reference: source.reference,
|
|
4125
|
+
digest: source.digest
|
|
4126
|
+
},
|
|
4127
|
+
destination
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
signal.throwIfAborted();
|
|
4131
|
+
context.assertWritable?.();
|
|
4132
|
+
commitAttempted = true;
|
|
4133
|
+
const mutation = await runtimeMemory.mutateResult("compact-and-mutate", {
|
|
4134
|
+
revision: plan.revision,
|
|
4135
|
+
mutation: request,
|
|
4136
|
+
compacted: compactedEntries,
|
|
4137
|
+
maxBytes: budget,
|
|
4138
|
+
lineage
|
|
4139
|
+
}, signal);
|
|
4140
|
+
if (provider === "host") {
|
|
4141
|
+
this.counters.migrations += 1;
|
|
4142
|
+
this.counters.lastRunId = runId;
|
|
4143
|
+
this.counters.lastOperation = "migration";
|
|
4144
|
+
this.counters.lastAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4145
|
+
}
|
|
4146
|
+
return {
|
|
4147
|
+
...mutation.value,
|
|
4148
|
+
revision: mutation.revision,
|
|
4149
|
+
maintenance: {
|
|
4150
|
+
kind: "mnemon-archive",
|
|
4151
|
+
runId,
|
|
4152
|
+
provider,
|
|
4153
|
+
summary,
|
|
4154
|
+
memoryBodyIds: [...memoryBodyIds]
|
|
4155
|
+
}
|
|
4156
|
+
};
|
|
4157
|
+
} catch (error) {
|
|
4158
|
+
if (created.size > 0) {
|
|
4159
|
+
const cleanupSignal = AbortSignal.timeout(3e4);
|
|
4160
|
+
const failures = [];
|
|
4161
|
+
if (commitAttempted) try {
|
|
4162
|
+
if ((await context.inspectRuntime.read("maintenance-plan", request, cleanupSignal)).revision !== plan.revision) throw new Error("Runtime revision changed; preserve archive entries and inspect the completed operation");
|
|
4163
|
+
} catch (inspectionError) {
|
|
4164
|
+
throw new AggregateError([error, inspectionError], `runtime archive failed with uncertain local commit; preserve Memory Space entries: ${[...created.values()].map((item) => `${item.memoryBodyId}/${item.id}`).join(", ")}`);
|
|
4165
|
+
}
|
|
4166
|
+
for (const entry of [...created.values()].reverse()) try {
|
|
4167
|
+
if (!mutationResultCommitted(await archive.cleanup.mutate("forget", entry, cleanupSignal))) throw new Error("Provider did not confirm archive cleanup");
|
|
4168
|
+
} catch (cleanupError) {
|
|
4169
|
+
failures.push(new Error(`Memory Space ${entry.memoryBodyId}, archive entry ${entry.id}`, { cause: cleanupError }));
|
|
4170
|
+
}
|
|
4171
|
+
if (failures.length > 0) throw new AggregateError([error, ...failures], `runtime archive failed and cleanup failed; ${failures.map((failure) => failure.message).join("; ")}`);
|
|
4172
|
+
}
|
|
4173
|
+
throw error;
|
|
4174
|
+
}
|
|
3949
4175
|
}
|
|
3950
4176
|
async compactUserAndCommit(context, request, plan, signal) {
|
|
3951
4177
|
const runtimeMemory = context.runtime;
|
|
@@ -4033,10 +4259,12 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
|
|
|
4033
4259
|
};
|
|
4034
4260
|
}
|
|
4035
4261
|
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery) {
|
|
4262
|
+
if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
|
|
4036
4263
|
const provider = this.provider(preferredProvider);
|
|
4037
4264
|
assertDshOutputSchema(outputSchema);
|
|
4038
4265
|
if (this.resultRuntime === void 0) throw new Error("dsh-mnemon subagent result tool runtime is unavailable");
|
|
4039
|
-
const resultToolName =
|
|
4266
|
+
const resultToolName = RESULT_TOOL_NAME;
|
|
4267
|
+
const requestId = randomUUID();
|
|
4040
4268
|
let captured;
|
|
4041
4269
|
let pending;
|
|
4042
4270
|
let activeResultExecution;
|
|
@@ -4046,7 +4274,6 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
|
|
|
4046
4274
|
const stagedReceipts = /* @__PURE__ */ new Map();
|
|
4047
4275
|
let run;
|
|
4048
4276
|
let failure;
|
|
4049
|
-
let disposeResultTool;
|
|
4050
4277
|
let disposeResultObserver;
|
|
4051
4278
|
let releaseWorkflow;
|
|
4052
4279
|
try {
|
|
@@ -4055,7 +4282,9 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
|
|
|
4055
4282
|
releaseWorkflow = execution.release;
|
|
4056
4283
|
signal = execution.signal;
|
|
4057
4284
|
}
|
|
4285
|
+
if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
|
|
4058
4286
|
const observer = this.resultRuntime.on("tools/result", ((execution, result) => {
|
|
4287
|
+
if (signal.aborted || !this.resultRequests.has(requestId)) return;
|
|
4059
4288
|
if (execution.token !== void 0) {
|
|
4060
4289
|
const entries = stagedReceipts.get(execution.token);
|
|
4061
4290
|
if (entries !== void 0) {
|
|
@@ -4099,37 +4328,27 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
|
|
|
4099
4328
|
}));
|
|
4100
4329
|
if (typeof observer !== "function") throw new Error("dsh-mnemon subagent result observer registration did not return a disposer");
|
|
4101
4330
|
disposeResultObserver = observer;
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
if (execution.concludeTurn === void 0) throw new Error("Mnemon subagent result tool requires terminal tool-call support");
|
|
4118
|
-
assertDshOutputValue(outputSchema, args);
|
|
4119
|
-
activeResultExecution = execution;
|
|
4120
|
-
staged.set(execution, {
|
|
4121
|
-
agentId: agent.id,
|
|
4122
|
-
value: args
|
|
4123
|
-
});
|
|
4124
|
-
execution.concludeTurn();
|
|
4125
|
-
return { recorded: true };
|
|
4126
|
-
}
|
|
4331
|
+
this.resultRequests.set(requestId, async (value, execution) => {
|
|
4332
|
+
signal.throwIfAborted();
|
|
4333
|
+
const agent = execution.agent;
|
|
4334
|
+
if (agent === void 0 || !isSubagent(agent)) throw new Error("Mnemon subagent result tools are restricted to delegated children");
|
|
4335
|
+
if (run !== void 0 && agent.id !== run.id) throw new Error("Mnemon subagent result belongs to a different child");
|
|
4336
|
+
if (activeResultExecution !== void 0 || pending !== void 0 || captured !== void 0) throw new Error("Mnemon subagent result was already recorded");
|
|
4337
|
+
if (execution.concludeTurn === void 0) throw new Error("Mnemon subagent result tool requires terminal tool-call support");
|
|
4338
|
+
assertDshOutputValue(outputSchema, value);
|
|
4339
|
+
activeResultExecution = execution;
|
|
4340
|
+
staged.set(execution, {
|
|
4341
|
+
agentId: agent.id,
|
|
4342
|
+
value
|
|
4343
|
+
});
|
|
4344
|
+
execution.concludeTurn();
|
|
4345
|
+
return { recorded: true };
|
|
4127
4346
|
});
|
|
4128
|
-
if (typeof registration !== "function") throw new Error("dsh-mnemon subagent result tool registration did not return a disposer");
|
|
4129
|
-
disposeResultTool = registration;
|
|
4130
4347
|
const completionPersona = `${persona}
|
|
4131
4348
|
|
|
4132
|
-
Completion protocol: call \`${resultToolName}\` exactly once with
|
|
4349
|
+
Completion protocol: call \`${resultToolName}\` exactly once with requestId \`${requestId}\` and result matching this JSON schema:
|
|
4350
|
+
${JSON.stringify(outputSchema)}
|
|
4351
|
+
This is the only completion channel for this run. Do not finish with a plain-text answer. The requestId expires when this run finishes or is cancelled.`;
|
|
4133
4352
|
const perOpMaxTokens = operation === "migration" || operation === "compaction" ? this.runtimeMaintenanceMaxTokensResolver?.() ?? 8192 : operation === "document-archive" ? 8192 : operation === "metadata-maintenance" ? 4096 : void 0;
|
|
4134
4353
|
const fixed = this.taskAgentModelResolver?.();
|
|
4135
4354
|
const baseAgentOptions = perOpMaxTokens === void 0 ? void 0 : { maxTokens: perOpMaxTokens };
|
|
@@ -4138,7 +4357,7 @@ Completion protocol: call \`${resultToolName}\` exactly once with the final resu
|
|
|
4138
4357
|
provider: fixed.provider,
|
|
4139
4358
|
model: fixed.model
|
|
4140
4359
|
};
|
|
4141
|
-
|
|
4360
|
+
const start = () => this.subagents.start(provider, {
|
|
4142
4361
|
label,
|
|
4143
4362
|
prompt: [{
|
|
4144
4363
|
type: "text",
|
|
@@ -4151,6 +4370,7 @@ Completion protocol: call \`${resultToolName}\` exactly once with the final resu
|
|
|
4151
4370
|
toolFilter: { allow: [...tools, resultToolName] },
|
|
4152
4371
|
persona: completionPersona
|
|
4153
4372
|
});
|
|
4373
|
+
run = operation === "review" ? await startGuardedReview(this.resultRuntime, parent, [...tools, resultToolName], start) : await start();
|
|
4154
4374
|
const activeRun = run;
|
|
4155
4375
|
const result = await activeRun.result;
|
|
4156
4376
|
if (captured !== void 0 && captured.agentId !== activeRun.id) throw new Error("Mnemon subagent result was recorded by a different child");
|
|
@@ -4180,17 +4400,13 @@ Completion protocol: call \`${resultToolName}\` exactly once with the final resu
|
|
|
4180
4400
|
failure = error;
|
|
4181
4401
|
throw error;
|
|
4182
4402
|
} finally {
|
|
4403
|
+
this.resultRequests.delete(requestId);
|
|
4183
4404
|
let cleanupFailure;
|
|
4184
4405
|
if (run !== void 0) try {
|
|
4185
4406
|
await run.dispose();
|
|
4186
4407
|
} catch (error) {
|
|
4187
4408
|
if (failure === void 0) cleanupFailure = error;
|
|
4188
4409
|
}
|
|
4189
|
-
if (disposeResultTool !== void 0) try {
|
|
4190
|
-
await disposeResultTool();
|
|
4191
|
-
} catch (error) {
|
|
4192
|
-
if (failure === void 0 && cleanupFailure === void 0) cleanupFailure = error;
|
|
4193
|
-
}
|
|
4194
4410
|
if (disposeResultObserver !== void 0) try {
|
|
4195
4411
|
await disposeResultObserver();
|
|
4196
4412
|
} catch (error) {
|
|
@@ -4925,6 +5141,7 @@ var MnemonAgentLifecycle = class {
|
|
|
4925
5141
|
this.primePending = true;
|
|
4926
5142
|
this.cueInjected = false;
|
|
4927
5143
|
this.injectedMemoryText = void 0;
|
|
5144
|
+
this.lastError = void 0;
|
|
4928
5145
|
this.mark("prime");
|
|
4929
5146
|
})),
|
|
4930
5147
|
this.agent.ctx.on("session/event", ((session, event) => this.sessionEvent(session, event))),
|
|
@@ -5104,6 +5321,7 @@ var MnemonAgentLifecycle = class {
|
|
|
5104
5321
|
this.lastReviewScore = triggeredScore;
|
|
5105
5322
|
this.lastReviewDocumentIds = result.documentIds;
|
|
5106
5323
|
this.turnActivity.clear();
|
|
5324
|
+
this.lastError = void 0;
|
|
5107
5325
|
this.mark("review");
|
|
5108
5326
|
} catch (error) {
|
|
5109
5327
|
if (!controller.signal.aborted) this.fail(error);
|
|
@@ -5183,13 +5401,13 @@ var MnemonAgentLifecycle = class {
|
|
|
5183
5401
|
mark(phase) {
|
|
5184
5402
|
this.lastPhase = phase;
|
|
5185
5403
|
this.lastAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5186
|
-
this.lastError = void 0;
|
|
5187
5404
|
}
|
|
5188
5405
|
fail(error) {
|
|
5189
5406
|
this.counters.failures += 1;
|
|
5190
5407
|
this.lastPhase = "error";
|
|
5191
5408
|
this.lastAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5192
|
-
this.lastError = error instanceof Error ? error.message : String(error);
|
|
5409
|
+
this.lastError = (error instanceof Error ? error.message : String(error)).replace(/\bsk-[A-Za-z0-9_-]{8,}\b/gu, "[redacted]").replace(/\s+/gu, " ").trim().slice(0, 500);
|
|
5410
|
+
console.warn(`[dsh-mnemon] idle review failed: ${this.lastError}`);
|
|
5193
5411
|
}
|
|
5194
5412
|
};
|
|
5195
5413
|
/** DSH-native owner for per-agent Mnemon lifecycle hooks and UI-triggered LLM work. */
|
|
@@ -9129,8 +9347,9 @@ function apply(rawContext, config = {}) {
|
|
|
9129
9347
|
const lifecycle = new MnemonLifecycle(ctx, coordinator, runtime.config, runtime);
|
|
9130
9348
|
ctx.effect(() => {
|
|
9131
9349
|
const stop = lifecycle.start();
|
|
9132
|
-
return () => {
|
|
9350
|
+
return async () => {
|
|
9133
9351
|
stop();
|
|
9352
|
+
await coordinator.dispose();
|
|
9134
9353
|
disposePrepared();
|
|
9135
9354
|
runtime.dispose();
|
|
9136
9355
|
};
|