dsh-mnemon 0.5.40 → 0.5.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bundle.js +1 -1
- package/lib/client.js +97 -4
- package/lib/index.js +760 -158
- package/lib/{starter-resolution-C5nNk-cs.js → starter-resolution-B5mdKlvf.js} +3 -3
- package/lib/starter.js +1 -1
- package/lib/types/src/client/locales.d.ts +11 -0
- package/lib/types/src/host/protocol.d.ts +7 -0
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -389,6 +389,8 @@ const DEFAULT_SKILLS_CONFIG = Object.freeze({
|
|
|
389
389
|
allowUpdate: true,
|
|
390
390
|
targetScope: "user",
|
|
391
391
|
autoBacktest: true,
|
|
392
|
+
draftMaxTokens: 32768,
|
|
393
|
+
draftRetryMaxTokens: 65536,
|
|
392
394
|
hints: true,
|
|
393
395
|
sidebar: false,
|
|
394
396
|
reviewThreshold: 5,
|
|
@@ -417,7 +419,7 @@ const schema = z.object({
|
|
|
417
419
|
const legacySchema = z.object({ sources: z.dict(z.object({ enabled: z.boolean() })).default({}) });
|
|
418
420
|
const ENTRY_ID$1 = /^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,299}$/u;
|
|
419
421
|
const clone$2 = (value) => JSON.parse(JSON.stringify(value));
|
|
420
|
-
function record$
|
|
422
|
+
function record$11(value) {
|
|
421
423
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Plugin configuration must be an object");
|
|
422
424
|
const json = JSON.stringify(value);
|
|
423
425
|
if (json.length > 65536) throw new Error("Plugin configuration exceeds 64 KiB");
|
|
@@ -431,13 +433,13 @@ function record$10(value) {
|
|
|
431
433
|
}
|
|
432
434
|
function preferences(value) {
|
|
433
435
|
if (value.strategyTypeId !== void 0 && !/^[a-z][a-z0-9-]{0,127}$/u.test(value.strategyTypeId)) throw new Error("Invalid Strategy type id");
|
|
434
|
-
const entries = record$
|
|
436
|
+
const entries = record$11(value.entries ?? {});
|
|
435
437
|
if (Object.keys(entries).length > 64) throw new Error("At most 64 memory plugin Entries can be configured");
|
|
436
438
|
for (const [entryId, item] of Object.entries(entries)) {
|
|
437
439
|
if (!ENTRY_ID$1.test(entryId)) throw new Error("Invalid memory plugin Entry id");
|
|
438
|
-
const candidate = record$
|
|
440
|
+
const candidate = record$11(item);
|
|
439
441
|
if (candidate.enabled !== void 0 && typeof candidate.enabled !== "boolean") throw new Error("Plugin enabled must be boolean");
|
|
440
|
-
record$
|
|
442
|
+
record$11(candidate.config);
|
|
441
443
|
}
|
|
442
444
|
return clone$2(value);
|
|
443
445
|
}
|
|
@@ -481,6 +483,8 @@ const SkillsConfig = z.object({
|
|
|
481
483
|
"project"
|
|
482
484
|
]).default(DEFAULT_SKILLS_CONFIG.targetScope),
|
|
483
485
|
autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
|
|
486
|
+
draftMaxTokens: z.number().step(1).min(128).max(131072).default(DEFAULT_SKILLS_CONFIG.draftMaxTokens),
|
|
487
|
+
draftRetryMaxTokens: z.number().step(1).min(128).max(131072).default(DEFAULT_SKILLS_CONFIG.draftRetryMaxTokens),
|
|
484
488
|
hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
|
|
485
489
|
sidebar: z.boolean().default(DEFAULT_SKILLS_CONFIG.sidebar),
|
|
486
490
|
reviewThreshold: z.number().step(1).min(1).max(50).default(DEFAULT_SKILLS_CONFIG.reviewThreshold),
|
|
@@ -640,6 +644,7 @@ const Config = z.object({
|
|
|
640
644
|
skills: SkillsConfig.default({ ...DEFAULT_SKILLS_CONFIG }),
|
|
641
645
|
persistenceStrategy: MemoryPersistenceStrategySchema,
|
|
642
646
|
taskAgentModel: TaskAgentModelSchema,
|
|
647
|
+
subagentProviderNames: z.array(z.string()).default([]),
|
|
643
648
|
syncEnabled: z.boolean().default(false)
|
|
644
649
|
});
|
|
645
650
|
function resolveInteractionConfig(config = {}) {
|
|
@@ -689,6 +694,11 @@ function resolveCustomPacks(value, legacyDataDir) {
|
|
|
689
694
|
}
|
|
690
695
|
return packs;
|
|
691
696
|
}
|
|
697
|
+
function resolveSubagentProviderNames(value) {
|
|
698
|
+
if (value === void 0) return [];
|
|
699
|
+
if (!Array.isArray(value) || value.length > 32 || value.some((name) => typeof name !== "string" || !name.trim() || name.length > 128)) throw new Error("dsh-mnemon: subagentProviderNames must contain at most 32 nonempty provider names of at most 128 characters");
|
|
700
|
+
return [...new Set(value.map((name) => name.trim()))];
|
|
701
|
+
}
|
|
692
702
|
function resolveTaskAgentModel(value) {
|
|
693
703
|
const mode = value?.mode ?? "inherit";
|
|
694
704
|
if (mode !== "inherit" && mode !== "fixed") throw new Error(`dsh-mnemon: unsupported task Agent model mode: ${String(mode)}`);
|
|
@@ -813,6 +823,7 @@ function resolveConfig(config = {}) {
|
|
|
813
823
|
},
|
|
814
824
|
persistenceStrategy: resolvePersistenceStrategy(config.persistenceStrategy),
|
|
815
825
|
taskAgentModel: resolveTaskAgentModel(config.taskAgentModel),
|
|
826
|
+
subagentProviderNames: resolveSubagentProviderNames(config.subagentProviderNames),
|
|
816
827
|
syncEnabled: config.syncEnabled ?? false
|
|
817
828
|
};
|
|
818
829
|
}
|
|
@@ -992,7 +1003,7 @@ function createStorageRoot(config, workspaceRoot) {
|
|
|
992
1003
|
const MNEMON_MACHINE_DIRECTORY = "state";
|
|
993
1004
|
const MNEMON_MACHINE_FILE = "machine.json";
|
|
994
1005
|
const MNEMON_MACHINE_VERSION = 1;
|
|
995
|
-
function record$
|
|
1006
|
+
function record$10(value) {
|
|
996
1007
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
997
1008
|
}
|
|
998
1009
|
/** The name a fresh identity carries: the host name, or a neutral fallback. */
|
|
@@ -1074,7 +1085,7 @@ var MnemonMachineStore = class {
|
|
|
1074
1085
|
} catch {
|
|
1075
1086
|
return;
|
|
1076
1087
|
}
|
|
1077
|
-
const parsed = record$
|
|
1088
|
+
const parsed = record$10(value);
|
|
1078
1089
|
if (parsed?.version !== MNEMON_MACHINE_VERSION) return void 0;
|
|
1079
1090
|
if (typeof parsed.id !== "string" || parsed.id.trim() === "") return void 0;
|
|
1080
1091
|
if (typeof parsed.label !== "string" || parsed.label.trim() === "") return void 0;
|
|
@@ -2048,7 +2059,7 @@ const COMPONENT_ORDER = MNEMON_PACK_COMPONENTS;
|
|
|
2048
2059
|
*/
|
|
2049
2060
|
const CONTENT_HASH = /^[a-f0-9]{64}$/u;
|
|
2050
2061
|
const SQLITE_HEADER = Buffer.from("SQLite format 3\0", "binary");
|
|
2051
|
-
function record$
|
|
2062
|
+
function record$9(value) {
|
|
2052
2063
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
2053
2064
|
}
|
|
2054
2065
|
function utf8(bytes, label) {
|
|
@@ -2111,7 +2122,7 @@ function componentsForScope(scope) {
|
|
|
2111
2122
|
}
|
|
2112
2123
|
/** Exported so the sync channel can read the manifest a commit holds without unpacking the archive. */
|
|
2113
2124
|
function parseManifest(value) {
|
|
2114
|
-
const manifest = record$
|
|
2125
|
+
const manifest = record$9(value);
|
|
2115
2126
|
if (manifest?.format !== "mnemonpack" || manifest.version !== 1) throw new Error("unsupported Mnemon Pack format or version");
|
|
2116
2127
|
if (manifest.scope !== "full" && !COMPONENT_ORDER.includes(manifest.scope)) throw new Error("Mnemon Pack scope is invalid");
|
|
2117
2128
|
if (typeof manifest.exportedAt !== "string") throw new Error("Mnemon Pack exportedAt is invalid");
|
|
@@ -2120,11 +2131,11 @@ function parseManifest(value) {
|
|
|
2120
2131
|
if (components.length === 0 || new Set(components).size !== components.length || components.some((component) => !COMPONENT_ORDER.includes(component))) throw new Error("Mnemon Pack components are invalid");
|
|
2121
2132
|
const expected = componentsForScope(manifest.scope);
|
|
2122
2133
|
if (components.length !== expected.length || expected.some((component) => !components.includes(component))) throw new Error("Mnemon Pack scope does not match its components");
|
|
2123
|
-
const source = record$
|
|
2134
|
+
const source = record$9(manifest.source);
|
|
2124
2135
|
if (source?.plugin !== "dsh-mnemon" || typeof source.pluginVersion !== "string") throw new Error("Mnemon Pack source is invalid");
|
|
2125
2136
|
if (!Array.isArray(manifest.summary)) throw new Error("Mnemon Pack summary is invalid");
|
|
2126
2137
|
const summary = manifest.summary.map((entry) => {
|
|
2127
|
-
const item = record$
|
|
2138
|
+
const item = record$9(entry);
|
|
2128
2139
|
const component = item?.component;
|
|
2129
2140
|
if (!COMPONENT_ORDER.includes(component) || !Number.isSafeInteger(item?.files) || !Number.isSafeInteger(item?.bytes) || !Number.isSafeInteger(item?.items) || Number(item?.files) < 0 || Number(item?.bytes) < 0 || Number(item?.items) < 0) throw new Error("Mnemon Pack summary entry is invalid");
|
|
2130
2141
|
return {
|
|
@@ -2150,7 +2161,7 @@ function parseManifest(value) {
|
|
|
2150
2161
|
/** The sync channel reads its machine identity with this parser; no Pack carries one. */
|
|
2151
2162
|
function parseMachineIdentity(value) {
|
|
2152
2163
|
if (value === void 0 || value === null) return void 0;
|
|
2153
|
-
const machine = record$
|
|
2164
|
+
const machine = record$9(value);
|
|
2154
2165
|
if (typeof machine?.id !== "string" || machine.id.trim() === "" || typeof machine.label !== "string" || typeof machine.createdAt !== "string") throw new Error("Mnemon Pack machine identity is invalid");
|
|
2155
2166
|
return {
|
|
2156
2167
|
id: machine.id,
|
|
@@ -2160,7 +2171,7 @@ function parseMachineIdentity(value) {
|
|
|
2160
2171
|
}
|
|
2161
2172
|
function parseEntryOrigin(value) {
|
|
2162
2173
|
if (value === void 0 || value === null) return void 0;
|
|
2163
|
-
const origin = record$
|
|
2174
|
+
const origin = record$9(value);
|
|
2164
2175
|
if (typeof origin?.machine !== "string" || typeof origin.label !== "string" || typeof origin.at !== "string") return void 0;
|
|
2165
2176
|
return {
|
|
2166
2177
|
machine: origin.machine,
|
|
@@ -2205,8 +2216,8 @@ function parseArchive(base64, runtimeLimits = RUNTIME_MEMORY_LIMITS) {
|
|
|
2205
2216
|
if (path !== "manifest.json" && path !== "checksums.json" && !allowedPayloadPath(path)) throw new Error(`unsupported Mnemon Pack entry: ${path}`);
|
|
2206
2217
|
}
|
|
2207
2218
|
const manifest = parseManifest(json$1(files["manifest.json"], "manifest.json"));
|
|
2208
|
-
const checksumValue = record$
|
|
2209
|
-
const checksumFiles = record$
|
|
2219
|
+
const checksumValue = record$9(json$1(files["checksums.json"], "checksums.json"));
|
|
2220
|
+
const checksumFiles = record$9(checksumValue?.files);
|
|
2210
2221
|
if (checksumValue?.algorithm !== "sha256" || checksumFiles === void 0) throw new Error("Mnemon Pack checksums are invalid");
|
|
2211
2222
|
const payloadNames = names.filter((path) => path.startsWith("payload/")).sort();
|
|
2212
2223
|
if (Object.keys(checksumFiles).length !== payloadNames.length) throw new Error("Mnemon Pack checksum inventory does not match the payload");
|
|
@@ -2229,10 +2240,10 @@ function parseArchive(base64, runtimeLimits = RUNTIME_MEMORY_LIMITS) {
|
|
|
2229
2240
|
};
|
|
2230
2241
|
}
|
|
2231
2242
|
function parseRuntime(value, limits = RUNTIME_MEMORY_LIMITS) {
|
|
2232
|
-
const source = record$
|
|
2243
|
+
const source = record$9(value);
|
|
2233
2244
|
if (source?.version !== RUNTIME_MEMORY_VERSION || !Array.isArray(source.entries)) throw new Error("runtime memories.json is invalid");
|
|
2234
2245
|
const entries = source.entries.map((raw) => {
|
|
2235
|
-
const entry = record$
|
|
2246
|
+
const entry = record$9(raw);
|
|
2236
2247
|
if (typeof entry?.content !== "string" || entry.target !== "memory" && entry.target !== "user" || ![
|
|
2237
2248
|
"critical",
|
|
2238
2249
|
"normal",
|
|
@@ -2269,12 +2280,12 @@ function emptyTombstones() {
|
|
|
2269
2280
|
};
|
|
2270
2281
|
}
|
|
2271
2282
|
function parseTombstones(value) {
|
|
2272
|
-
const source = record$
|
|
2283
|
+
const source = record$9(value);
|
|
2273
2284
|
if (source?.version !== 1 || !Array.isArray(source.tombstones)) throw new Error("runtime tombstones.json is invalid");
|
|
2274
2285
|
return {
|
|
2275
2286
|
version: 1,
|
|
2276
2287
|
tombstones: source.tombstones.map((raw) => {
|
|
2277
|
-
const tombstone = record$
|
|
2288
|
+
const tombstone = record$9(raw);
|
|
2278
2289
|
if (tombstone?.target !== "memory" && tombstone?.target !== "user") throw new Error("runtime tombstones.json contains an invalid entry");
|
|
2279
2290
|
if (typeof tombstone.contentHash !== "string" || !CONTENT_HASH.test(tombstone.contentHash) || typeof tombstone.deletedAt !== "string") throw new Error("runtime tombstones.json contains an invalid entry");
|
|
2280
2291
|
return {
|
|
@@ -2689,11 +2700,11 @@ function emptyRuntimeIndex() {
|
|
|
2689
2700
|
}
|
|
2690
2701
|
/** A damaged index only costs forgotten deletions, so it degrades instead of failing an export. */
|
|
2691
2702
|
function parseRuntimeIndex(value) {
|
|
2692
|
-
const source = record$
|
|
2703
|
+
const source = record$9(value);
|
|
2693
2704
|
if (source?.version !== 1 || !Array.isArray(source.entries)) return emptyRuntimeIndex();
|
|
2694
2705
|
const entries = [];
|
|
2695
2706
|
for (const raw of source.entries) {
|
|
2696
|
-
const entry = record$
|
|
2707
|
+
const entry = record$9(raw);
|
|
2697
2708
|
if (entry?.target !== "memory" && entry?.target !== "user") continue;
|
|
2698
2709
|
if (typeof entry.contentHash !== "string" || !/^[a-f0-9]{64}$/u.test(entry.contentHash)) continue;
|
|
2699
2710
|
entries.push({
|
|
@@ -3345,7 +3356,7 @@ const STATUSES = [
|
|
|
3345
3356
|
"accepted",
|
|
3346
3357
|
"rejected"
|
|
3347
3358
|
];
|
|
3348
|
-
function record$
|
|
3359
|
+
function record$8(value) {
|
|
3349
3360
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
3350
3361
|
}
|
|
3351
3362
|
function text$2(value, limit = MAX_REVIEW_TEXT) {
|
|
@@ -3357,7 +3368,7 @@ function strings$1(value, limit = 64) {
|
|
|
3357
3368
|
function parseOpinions(value) {
|
|
3358
3369
|
if (!Array.isArray(value)) return [];
|
|
3359
3370
|
return value.flatMap((raw) => {
|
|
3360
|
-
const opinion = record$
|
|
3371
|
+
const opinion = record$8(raw);
|
|
3361
3372
|
if (opinion === void 0 || typeof opinion.id !== "string" || typeof opinion.text !== "string") return [];
|
|
3362
3373
|
return [{
|
|
3363
3374
|
id: opinion.id,
|
|
@@ -3369,11 +3380,11 @@ function parseOpinions(value) {
|
|
|
3369
3380
|
}
|
|
3370
3381
|
/** A proposal that no longer parses is dropped rather than failing the whole ledger. */
|
|
3371
3382
|
function parseEntry(value) {
|
|
3372
|
-
const entry = record$
|
|
3383
|
+
const entry = record$8(value);
|
|
3373
3384
|
if (entry === void 0 || typeof entry.id !== "string" || entry.id.trim() === "") return void 0;
|
|
3374
3385
|
if (typeof entry.status !== "string" || !STATUSES.includes(entry.status)) return void 0;
|
|
3375
3386
|
if (!Array.isArray(entry.operations)) return void 0;
|
|
3376
|
-
const machine = record$
|
|
3387
|
+
const machine = record$8(entry.machine) ?? {};
|
|
3377
3388
|
const createdAt = typeof entry.createdAt === "string" ? entry.createdAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
3378
3389
|
return {
|
|
3379
3390
|
id: entry.id,
|
|
@@ -3523,7 +3534,7 @@ var MnemonReviewLedger = class {
|
|
|
3523
3534
|
version: 1,
|
|
3524
3535
|
entries: []
|
|
3525
3536
|
};
|
|
3526
|
-
const file = record$
|
|
3537
|
+
const file = record$8(JSON.parse(readFileSync(this.path(), "utf8")));
|
|
3527
3538
|
if (file?.version !== 1 || !Array.isArray(file.entries)) throw new Error("review ledger is invalid: " + this.path());
|
|
3528
3539
|
return {
|
|
3529
3540
|
version: 1,
|
|
@@ -3974,7 +3985,7 @@ const MNEMON_GITHUB_MAX_INTERVAL_MS = 6e4;
|
|
|
3974
3985
|
const MNEMON_GITHUB_FLOW_LIMIT_MS = 36e5;
|
|
3975
3986
|
const MNEMON_GITHUB_USER_AGENT = "dsh-mnemon-sync";
|
|
3976
3987
|
const DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
3977
|
-
function record$
|
|
3988
|
+
function record$7(value) {
|
|
3978
3989
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
3979
3990
|
}
|
|
3980
3991
|
function text$1(value) {
|
|
@@ -3982,7 +3993,7 @@ function text$1(value) {
|
|
|
3982
3993
|
}
|
|
3983
3994
|
/** GitHub answers a refused request with a `error` code or an API `message`. */
|
|
3984
3995
|
function codeOf(body) {
|
|
3985
|
-
const fields = record$
|
|
3996
|
+
const fields = record$7(body);
|
|
3986
3997
|
return text$1(fields?.error) ?? text$1(fields?.message);
|
|
3987
3998
|
}
|
|
3988
3999
|
/** A positive count GitHub sends as a JSON number, or as a string in a form-encoded answer. */
|
|
@@ -3997,7 +4008,7 @@ const FIELD_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/u;
|
|
|
3997
4008
|
* device code or a token. Enough to tell a proxy's HTML page from a reply that lost a field.
|
|
3998
4009
|
*/
|
|
3999
4010
|
function shapeOf(answer) {
|
|
4000
|
-
const keys = Object.keys(record$
|
|
4011
|
+
const keys = Object.keys(record$7(answer.body) ?? {});
|
|
4001
4012
|
const names = keys.filter((key) => FIELD_NAME.test(key)).slice(0, 8);
|
|
4002
4013
|
const listed = keys.length > names.length ? [...names, String(keys.length - names.length) + " other"] : names;
|
|
4003
4014
|
return "HTTP " + String(answer.status) + ", content-type " + (answer.type ?? "none") + ", fields: " + (listed.length === 0 ? "none" : listed.join(", "));
|
|
@@ -4009,9 +4020,9 @@ function failure$3(action, status, body) {
|
|
|
4009
4020
|
}
|
|
4010
4021
|
/** Read one grant out of a stored record, refusing anything else. */
|
|
4011
4022
|
function githubGrantFromRecord(record_) {
|
|
4012
|
-
const fields = record$
|
|
4023
|
+
const fields = record$7(record_);
|
|
4013
4024
|
if (fields?.kind !== "grant") return void 0;
|
|
4014
|
-
const payload = record$
|
|
4025
|
+
const payload = record$7(fields.payload);
|
|
4015
4026
|
if (payload?.version !== 1) return void 0;
|
|
4016
4027
|
const accessToken = text$1(payload.accessToken);
|
|
4017
4028
|
if (accessToken === void 0) return void 0;
|
|
@@ -4025,13 +4036,13 @@ function githubGrantFromRecord(record_) {
|
|
|
4025
4036
|
};
|
|
4026
4037
|
}
|
|
4027
4038
|
function repositoryOf(value) {
|
|
4028
|
-
const fields = record$
|
|
4039
|
+
const fields = record$7(value);
|
|
4029
4040
|
const fullName = text$1(fields?.full_name);
|
|
4030
4041
|
const url = text$1(fields?.clone_url);
|
|
4031
4042
|
const name = text$1(fields?.name);
|
|
4032
4043
|
if (fullName === void 0 || url === void 0 || name === void 0) return void 0;
|
|
4033
|
-
const owner = record$
|
|
4034
|
-
const permissions = record$
|
|
4044
|
+
const owner = record$7(fields?.owner);
|
|
4045
|
+
const permissions = record$7(fields?.permissions);
|
|
4035
4046
|
return {
|
|
4036
4047
|
name,
|
|
4037
4048
|
fullName,
|
|
@@ -4144,7 +4155,7 @@ var MnemonGitHubAuth = class {
|
|
|
4144
4155
|
throw new Error("the GitHub device code request could not reach GitHub: " + describeTransportError(error));
|
|
4145
4156
|
}
|
|
4146
4157
|
if (answer.status !== 200) throw failure$3("the GitHub device code request", answer.status, answer.body);
|
|
4147
|
-
const fields = record$
|
|
4158
|
+
const fields = record$7(answer.body);
|
|
4148
4159
|
const deviceCode = text$1(fields?.device_code);
|
|
4149
4160
|
const userCode = text$1(fields?.user_code);
|
|
4150
4161
|
const verificationUri = text$1(fields?.verification_uri);
|
|
@@ -4193,7 +4204,7 @@ var MnemonGitHubAuth = class {
|
|
|
4193
4204
|
message: describeTransportError(error)
|
|
4194
4205
|
};
|
|
4195
4206
|
}
|
|
4196
|
-
const fields = record$
|
|
4207
|
+
const fields = record$7(answer.body);
|
|
4197
4208
|
const code = text$1(fields?.error);
|
|
4198
4209
|
if (code === void 0) {
|
|
4199
4210
|
const accessToken = text$1(fields?.access_token);
|
|
@@ -4264,7 +4275,7 @@ var MnemonGitHubAuth = class {
|
|
|
4264
4275
|
...signal === void 0 ? {} : { signal }
|
|
4265
4276
|
});
|
|
4266
4277
|
if (status !== 200) return void 0;
|
|
4267
|
-
return text$1(record$
|
|
4278
|
+
return text$1(record$7(body)?.login);
|
|
4268
4279
|
} catch {
|
|
4269
4280
|
return;
|
|
4270
4281
|
}
|
|
@@ -4390,7 +4401,7 @@ const SETTING_KEYS = [
|
|
|
4390
4401
|
"autoBackupMinutes"
|
|
4391
4402
|
];
|
|
4392
4403
|
const REPOSITORY_HINT = "sync repository must be an https:// or ssh:// URL, a git@host:path address, or an absolute local path";
|
|
4393
|
-
function record$
|
|
4404
|
+
function record$6(value) {
|
|
4394
4405
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
4395
4406
|
}
|
|
4396
4407
|
function known(key) {
|
|
@@ -4494,7 +4505,7 @@ var MnemonSyncSettingsStore = class {
|
|
|
4494
4505
|
} catch {
|
|
4495
4506
|
throw new Error("sync configuration is not valid JSON");
|
|
4496
4507
|
}
|
|
4497
|
-
const stored = record$
|
|
4508
|
+
const stored = record$6(parsed);
|
|
4498
4509
|
if (stored === void 0) throw new Error("sync configuration is not a JSON object");
|
|
4499
4510
|
for (const key of Object.keys(stored)) if (!known(key)) throw new Error("sync configuration has an unknown field: " + key);
|
|
4500
4511
|
const settings = this.defaults();
|
|
@@ -4541,7 +4552,7 @@ var MnemonSyncSettingsStore = class {
|
|
|
4541
4552
|
* turns the automatic backup off.
|
|
4542
4553
|
*/
|
|
4543
4554
|
patch(patch) {
|
|
4544
|
-
const fields = record$
|
|
4555
|
+
const fields = record$6(patch);
|
|
4545
4556
|
if (fields === void 0) throw new Error("sync configuration must be an object");
|
|
4546
4557
|
for (const key of Object.keys(fields)) if (!known(key)) throw new Error("unknown sync setting: " + key);
|
|
4547
4558
|
const next = { ...this.read() };
|
|
@@ -4777,7 +4788,7 @@ function remoteFailure(message) {
|
|
|
4777
4788
|
function rejectedPush(output) {
|
|
4778
4789
|
return /\[rejected\]|fetch first|non-fast-forward/iu.test(output);
|
|
4779
4790
|
}
|
|
4780
|
-
function record$
|
|
4791
|
+
function record$5(value) {
|
|
4781
4792
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
4782
4793
|
}
|
|
4783
4794
|
function tail(output) {
|
|
@@ -4842,7 +4853,7 @@ const TIMESTAMP_FIELDS = /* @__PURE__ */ new Set(["exportedAt", "pushedAt"]);
|
|
|
4842
4853
|
/** The same manifest without its timestamps describes the same payload. */
|
|
4843
4854
|
function withoutTimestamps(value) {
|
|
4844
4855
|
if (Array.isArray(value)) return value.map(withoutTimestamps);
|
|
4845
|
-
const parsed = record$
|
|
4856
|
+
const parsed = record$5(value);
|
|
4846
4857
|
if (parsed === void 0) return value;
|
|
4847
4858
|
return Object.fromEntries(Object.entries(parsed).filter(([key]) => !TIMESTAMP_FIELDS.has(key)).map(([key, item]) => [key, withoutTimestamps(item)]));
|
|
4848
4859
|
}
|
|
@@ -4855,7 +4866,7 @@ function manifestIdentity(bytes) {
|
|
|
4855
4866
|
} catch {
|
|
4856
4867
|
return;
|
|
4857
4868
|
}
|
|
4858
|
-
return record$
|
|
4869
|
+
return record$5(value) === void 0 ? void 0 : JSON.stringify(withoutTimestamps(value));
|
|
4859
4870
|
}
|
|
4860
4871
|
/**
|
|
4861
4872
|
* Whether the mirror already holds exactly this payload. Every push re-exports
|
|
@@ -5060,7 +5071,20 @@ function readFiles(root, limit = MAX_PAYLOAD_BYTES) {
|
|
|
5060
5071
|
function unpack(archive) {
|
|
5061
5072
|
let entries;
|
|
5062
5073
|
try {
|
|
5063
|
-
|
|
5074
|
+
if (archive.byteLength > 268435456) throw new Error("compressed archive exceeds safety limit");
|
|
5075
|
+
let expandedBytes = 0;
|
|
5076
|
+
let fileCount = 0;
|
|
5077
|
+
entries = unzipSync(archive, { filter(info) {
|
|
5078
|
+
const path = info.name;
|
|
5079
|
+
const normalized = path.endsWith("/") ? path.slice(0, -1) : path;
|
|
5080
|
+
if (path.includes("\\") || path.startsWith("/") || /^[a-z]:/i.test(path) || path.includes("\0") || normalized.split("/").some((part) => !part || part === "." || part === "..")) throw new Error("unsafe archive path");
|
|
5081
|
+
if (path.endsWith("/")) return false;
|
|
5082
|
+
if (info.compression === 0 && info.size !== info.originalSize) throw new Error("invalid stored archive entry size");
|
|
5083
|
+
expandedBytes += info.originalSize;
|
|
5084
|
+
fileCount += 1;
|
|
5085
|
+
if (!Number.isSafeInteger(info.originalSize) || info.originalSize < 0 || info.originalSize > 134217728 || expandedBytes > 268435456 || fileCount > 4096) throw new Error("expanded archive exceeds safety limit");
|
|
5086
|
+
return true;
|
|
5087
|
+
} });
|
|
5064
5088
|
} catch {
|
|
5065
5089
|
throw new Error("the Mnemon payload is not a readable archive");
|
|
5066
5090
|
}
|
|
@@ -5218,7 +5242,7 @@ var MnemonGitSync = class {
|
|
|
5218
5242
|
}
|
|
5219
5243
|
async configure(patch) {
|
|
5220
5244
|
const previous = this.store.read();
|
|
5221
|
-
const fields = record$
|
|
5245
|
+
const fields = record$5(patch);
|
|
5222
5246
|
const { machineLabel: name, ...settings } = fields ?? {};
|
|
5223
5247
|
const naming = fields !== void 0 && "machineLabel" in fields;
|
|
5224
5248
|
if (naming && name !== null && name !== "") machineLabel(name);
|
|
@@ -5426,7 +5450,7 @@ var MnemonGitSync = class {
|
|
|
5426
5450
|
/** When memory last travelled through this channel, or nothing when it never did. */
|
|
5427
5451
|
lastRun() {
|
|
5428
5452
|
try {
|
|
5429
|
-
const value = record$
|
|
5453
|
+
const value = record$5(JSON.parse(readFileSync(this.lastRunPath(), "utf8")));
|
|
5430
5454
|
if (value === void 0 || typeof value.at !== "string" || typeof value.commit !== "string") return void 0;
|
|
5431
5455
|
if (value.kind !== "sync" && value.kind !== "merge") return void 0;
|
|
5432
5456
|
return {
|
|
@@ -5464,13 +5488,13 @@ var MnemonGitSync = class {
|
|
|
5464
5488
|
async agreedDocuments(settings, tip, signal) {
|
|
5465
5489
|
let value;
|
|
5466
5490
|
try {
|
|
5467
|
-
value = record$
|
|
5491
|
+
value = record$5(JSON.parse(readFileSync(this.basePath(), "utf8")));
|
|
5468
5492
|
} catch {
|
|
5469
5493
|
value = void 0;
|
|
5470
5494
|
}
|
|
5471
5495
|
if (value === void 0) return this.lastRunDocuments(settings, tip, signal);
|
|
5472
5496
|
if (value.version !== 1 || value.scope !== this.agreementScope(settings)) return void 0;
|
|
5473
|
-
const documents = record$
|
|
5497
|
+
const documents = record$5(value.documents);
|
|
5474
5498
|
if (typeof value.commit !== "string" || !AGREED_COMMIT.test(value.commit) || documents === void 0) return void 0;
|
|
5475
5499
|
if ((await this.git([
|
|
5476
5500
|
"merge-base",
|
|
@@ -5917,9 +5941,9 @@ var MnemonGitSync = class {
|
|
|
5917
5941
|
let machine;
|
|
5918
5942
|
let pushedAt;
|
|
5919
5943
|
try {
|
|
5920
|
-
const raw = record$
|
|
5944
|
+
const raw = record$5(parseJson(bytes, "the backup manifest"));
|
|
5921
5945
|
if (raw === void 0) return void 0;
|
|
5922
|
-
const extension = record$
|
|
5946
|
+
const extension = record$5(raw[SYNC_EXTENSION]);
|
|
5923
5947
|
pushedAt = typeof extension?.pushedAt === "string" ? extension.pushedAt : void 0;
|
|
5924
5948
|
machine = extensionMachine(extension);
|
|
5925
5949
|
manifest = parseManifest(raw);
|
|
@@ -6226,11 +6250,11 @@ var MnemonGitSync = class {
|
|
|
6226
6250
|
if (manifestBytes === void 0 || checksumBytes === void 0) return void 0;
|
|
6227
6251
|
if (manifestBytes.byteLength > MAX_MANIFEST_BYTES) throw new Error("the remote Mnemon manifest exceeds the safety limit");
|
|
6228
6252
|
for (const path of Object.keys(files)) if (path !== MANIFEST && path !== CHECKSUMS && !path.startsWith("payload/")) throw new Error("the remote Mnemon payload holds an unexpected file: " + path);
|
|
6229
|
-
const parsed = record$
|
|
6253
|
+
const parsed = record$5(parseJson(manifestBytes, "the remote Mnemon manifest"));
|
|
6230
6254
|
if (parsed === void 0) throw new Error("the remote Mnemon manifest is not a JSON object");
|
|
6231
6255
|
if (parsed.format !== "mnemonpack" || parsed.version !== 1) throw new Error("the remote Mnemon manifest is not a supported Mnemon Pack");
|
|
6232
|
-
const checksums = record$
|
|
6233
|
-
const inventory = record$
|
|
6256
|
+
const checksums = record$5(parseJson(checksumBytes, "the remote Mnemon checksum inventory"));
|
|
6257
|
+
const inventory = record$5(checksums?.files);
|
|
6234
6258
|
if (checksums?.algorithm !== "sha256" || inventory === void 0) throw new Error("the remote Mnemon checksum inventory is invalid");
|
|
6235
6259
|
for (const [path, bytes] of Object.entries(files)) {
|
|
6236
6260
|
if (path === MANIFEST || path === CHECKSUMS) continue;
|
|
@@ -6239,7 +6263,7 @@ var MnemonGitSync = class {
|
|
|
6239
6263
|
if (expected !== sha256Hex(bytes)) throw new Error("the remote Mnemon payload failed its checksum: " + path);
|
|
6240
6264
|
}
|
|
6241
6265
|
for (const path of Object.keys(inventory)) if (files[path] === void 0) throw new Error("the remote Mnemon payload is missing a checksummed file: " + path);
|
|
6242
|
-
const extension = record$
|
|
6266
|
+
const extension = record$5(parsed[SYNC_EXTENSION]);
|
|
6243
6267
|
const pushedAt = typeof extension?.pushedAt === "string" ? extension.pushedAt : void 0;
|
|
6244
6268
|
const machine = extensionMachine(extension);
|
|
6245
6269
|
const archive = zipSync(files, {
|
|
@@ -6460,7 +6484,7 @@ function abandoned(path, now) {
|
|
|
6460
6484
|
}
|
|
6461
6485
|
if (age > STALE_LOCK_MS) return true;
|
|
6462
6486
|
try {
|
|
6463
|
-
const pid = record$
|
|
6487
|
+
const pid = record$5(JSON.parse(readFileSync(join(path, LOCK_OWNER), "utf8")))?.pid;
|
|
6464
6488
|
return typeof pid === "number" && Number.isInteger(pid) && pid > 0 && pid !== process.pid && !processAlive(pid);
|
|
6465
6489
|
} catch {
|
|
6466
6490
|
return age > OWNERLESS_LOCK_MS;
|
|
@@ -7187,9 +7211,9 @@ var MemoryExecutions = class {
|
|
|
7187
7211
|
constructor(runtime) {
|
|
7188
7212
|
this.runtime = runtime;
|
|
7189
7213
|
}
|
|
7190
|
-
async turn(agent, turn, signal, delegation) {
|
|
7214
|
+
async turn(agent, turn, signal, delegation, independent = false) {
|
|
7191
7215
|
signal.throwIfAborted();
|
|
7192
|
-
const slot = this.open(agent, "turn", `${agent.id}:${turn}`, "agent.root-turn", delegation);
|
|
7216
|
+
const slot = this.open(agent, "turn", `${agent.id}:${turn}`, "agent.root-turn", delegation, independent);
|
|
7193
7217
|
const abort = () => slot.controller.abort(signal.reason);
|
|
7194
7218
|
signal.addEventListener("abort", abort, { once: true });
|
|
7195
7219
|
try {
|
|
@@ -7272,7 +7296,7 @@ var MemoryExecutions = class {
|
|
|
7272
7296
|
if (slot.kind === "turn") slot.close();
|
|
7273
7297
|
}
|
|
7274
7298
|
}
|
|
7275
|
-
open(agent, kind, turnId, purpose, delegation) {
|
|
7299
|
+
open(agent, kind, turnId, purpose, delegation, independent = false) {
|
|
7276
7300
|
this.assertOpen();
|
|
7277
7301
|
const previous = this.slots.get(agent.id);
|
|
7278
7302
|
const done = deferred();
|
|
@@ -7307,7 +7331,7 @@ var MemoryExecutions = class {
|
|
|
7307
7331
|
await previous?.done;
|
|
7308
7332
|
slot.controller.signal.throwIfAborted();
|
|
7309
7333
|
this.assertOpen();
|
|
7310
|
-
const graph = delegation?.graph ?? this.runtime.forAgent(agent);
|
|
7334
|
+
const graph = delegation?.graph ?? (independent ? this.runtime.forAgent(agent, { independent: true }) : this.runtime.forAgent(agent));
|
|
7311
7335
|
const manager = graph.composableTurns;
|
|
7312
7336
|
const scope = delegation === void 0 ? agentScope(agent, graph.config) : {
|
|
7313
7337
|
...delegation.scope,
|
|
@@ -7619,13 +7643,15 @@ var LiveMnemonRuntime = class {
|
|
|
7619
7643
|
for (const graph of graphs) graph.dispose();
|
|
7620
7644
|
}
|
|
7621
7645
|
/** Resolve the runtime that must serve one Agent execution. */
|
|
7622
|
-
forAgent(agent) {
|
|
7646
|
+
forAgent(agent, options = {}) {
|
|
7623
7647
|
this.assertOpen();
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7648
|
+
if (!options.independent) {
|
|
7649
|
+
const pinned = this.agentGraphs.get(agent.id);
|
|
7650
|
+
if (pinned !== void 0) return pinned.graph;
|
|
7651
|
+
const parentSession = agent.session.header?.origin === "subagent" ? agent.session.header.parentSession?.trim() : void 0;
|
|
7652
|
+
const inherited = parentSession === void 0 || parentSession === "" ? void 0 : this.agentGraphs.get(parentSession);
|
|
7653
|
+
if (inherited !== void 0) return inherited.graph;
|
|
7654
|
+
}
|
|
7629
7655
|
if (!isWorkspaceStorageScope(this.current.config.storageScope)) return this.current;
|
|
7630
7656
|
const cwd = agent.session.header?.cwd?.trim();
|
|
7631
7657
|
if (cwd === void 0 || cwd === "") throw new Error("the current DSH session has no workspace for Mnemon");
|
|
@@ -7979,6 +8005,7 @@ function registerCommands(commands, service, coordinator) {
|
|
|
7979
8005
|
const GUIDANCE_SECTION_NAME = "mnemon:routing";
|
|
7980
8006
|
const RUNTIME_MEMORY_CONTEXT_NAME = "mnemon:runtime-memory";
|
|
7981
8007
|
const STRATEGY_SECTION_NAME = "mnemon:strategy";
|
|
8008
|
+
const SNAPSHOT_GUIDANCE = "Mnemon memory snapshots are complete projections in chronological order. The latest snapshot supersedes earlier snapshots as the current projected state; do not treat older versions as additional current facts or authority. Absence may reflect filtering or projection budgets and does not imply deletion from storage.";
|
|
7982
8009
|
const ROUTING_GUIDANCE = "Use memory only when needed. Follow the current Mnemon View: use mnemon_view_route or mnemon_view_action only for offered ids. Installed tools do not imply an available Source. Never infer missing historical facts. An action offer is not authorization; a write exists only after its receipt.";
|
|
7983
8010
|
/** A View is an own-plugin message, never part of the shared context snapshot. */
|
|
7984
8011
|
function withoutMemoryViewContext(assembly) {
|
|
@@ -7989,12 +8016,20 @@ function withoutMemoryViewContext(assembly) {
|
|
|
7989
8016
|
}
|
|
7990
8017
|
/** Inject trusted Strategy instructions after interpolation; Source text stays quoted in the Wake. */
|
|
7991
8018
|
function applyMemoryViewGuidance(assembly, view, routingEnabled = true) {
|
|
7992
|
-
if (view === void 0) return withoutMemoryViewContext(
|
|
8019
|
+
if (view === void 0) return withoutMemoryViewContext({
|
|
8020
|
+
...assembly,
|
|
8021
|
+
sections: assembly.sections.filter((section) => section.name !== "mnemon:snapshot-history")
|
|
8022
|
+
});
|
|
7993
8023
|
const sections = assembly.sections.filter((section) => ![
|
|
7994
8024
|
GUIDANCE_SECTION_NAME,
|
|
7995
8025
|
STRATEGY_SECTION_NAME,
|
|
7996
|
-
"mnemon:runtime-memory-protocol"
|
|
8026
|
+
"mnemon:runtime-memory-protocol",
|
|
8027
|
+
"mnemon:snapshot-history"
|
|
7997
8028
|
].includes(section.name));
|
|
8029
|
+
sections.push({
|
|
8030
|
+
name: "mnemon:snapshot-history",
|
|
8031
|
+
text: SNAPSHOT_GUIDANCE
|
|
8032
|
+
});
|
|
7998
8033
|
if (view.guidance?.system) sections.push({
|
|
7999
8034
|
name: STRATEGY_SECTION_NAME,
|
|
8000
8035
|
text: view.guidance.system
|
|
@@ -8130,7 +8165,7 @@ function pendingOpinions(entries) {
|
|
|
8130
8165
|
createdAt: opinion.createdAt
|
|
8131
8166
|
}))).sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
8132
8167
|
}
|
|
8133
|
-
function record$
|
|
8168
|
+
function record$4(value) {
|
|
8134
8169
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
8135
8170
|
}
|
|
8136
8171
|
function stringField(value) {
|
|
@@ -8279,7 +8314,7 @@ function body(value, index, field) {
|
|
|
8279
8314
|
return text.slice(0, MAX_FIELD);
|
|
8280
8315
|
}
|
|
8281
8316
|
function parseOperation(value, index, evidence) {
|
|
8282
|
-
const operation = record$
|
|
8317
|
+
const operation = record$4(value);
|
|
8283
8318
|
if (operation === void 0) throw new Error(`reconcile operation ${index} must be an object`);
|
|
8284
8319
|
const kind = stringField(operation.kind);
|
|
8285
8320
|
if (kind === void 0 || !KINDS.includes(kind)) throw new Error(`reconcile operation ${index} has an unsupported kind: ${JSON.stringify(operation.kind)}`);
|
|
@@ -8352,7 +8387,7 @@ function emptyDocumentSnapshot(directory, at) {
|
|
|
8352
8387
|
}
|
|
8353
8388
|
/** The host, not the model, decides what a proposal may contain. */
|
|
8354
8389
|
function parseReconcileResult(value, evidence) {
|
|
8355
|
-
const result = record$
|
|
8390
|
+
const result = record$4(value);
|
|
8356
8391
|
if (result === void 0) throw new Error("memory reconciliation returned an invalid structured result");
|
|
8357
8392
|
const action = stringField(result.action);
|
|
8358
8393
|
if (action === void 0 || ![
|
|
@@ -8461,6 +8496,72 @@ async function applyReconcileOperations(operations, applier, signal, selection)
|
|
|
8461
8496
|
};
|
|
8462
8497
|
}
|
|
8463
8498
|
//#endregion
|
|
8499
|
+
//#region src/host/agent-compat.ts
|
|
8500
|
+
function configuredProviderNames(host) {
|
|
8501
|
+
const names = /* @__PURE__ */ new Set();
|
|
8502
|
+
for (const runtime of host?.registry?.values() ?? []) for (const fiber of runtime.fibers) {
|
|
8503
|
+
const config = fiber.effectiveConfig;
|
|
8504
|
+
if (config && typeof config === "object" && "providerName" in config && typeof config.providerName === "string") names.add(config.providerName);
|
|
8505
|
+
}
|
|
8506
|
+
return [...names];
|
|
8507
|
+
}
|
|
8508
|
+
/** Official 0.2.2-rc.1 has registry ownership; master fcc0ad4 moved it into Subagent. */
|
|
8509
|
+
function activationOwned(subagents) {
|
|
8510
|
+
return typeof subagents?.rootOf === "function";
|
|
8511
|
+
}
|
|
8512
|
+
/** Only live registry instances can be adopted; persisted lineage is not authority. */
|
|
8513
|
+
function isRootAgent(agents, subagents, agent) {
|
|
8514
|
+
if (agents.get(agent.id) !== agent) return false;
|
|
8515
|
+
if (typeof agents.roots === "function") return agents.roots().includes(agent);
|
|
8516
|
+
try {
|
|
8517
|
+
return subagents.rootOf?.(agent) === agent;
|
|
8518
|
+
} catch {
|
|
8519
|
+
return false;
|
|
8520
|
+
}
|
|
8521
|
+
}
|
|
8522
|
+
function rootAgents(agents, subagents) {
|
|
8523
|
+
if (typeof agents.roots === "function") return agents.roots();
|
|
8524
|
+
return (agents.list?.() ?? []).filter((agent) => isRootAgent(agents, subagents, agent));
|
|
8525
|
+
}
|
|
8526
|
+
/** Persisted IDs keep completed internal sessions out of conversation automation after restart. */
|
|
8527
|
+
function isTaskRecord(id) {
|
|
8528
|
+
return /^mnemon-(?:task|run)-v1-[0-9a-f-]{36}$/.test(id);
|
|
8529
|
+
}
|
|
8530
|
+
function titleTask(agent, title) {
|
|
8531
|
+
(agent.ctx.get?.("sessionTitle"))?.rename?.(agent.session, title);
|
|
8532
|
+
}
|
|
8533
|
+
const creatingChild = new AsyncLocalStorage();
|
|
8534
|
+
/** Share one reservation across guard, continuation and composition wrappers, never across nested runs. */
|
|
8535
|
+
function withReservedChild(parent, operation, fresh = false) {
|
|
8536
|
+
const current = creatingChild.getStore();
|
|
8537
|
+
if (!fresh && current?.parent === parent && !current.claimed) return operation();
|
|
8538
|
+
return creatingChild.run({
|
|
8539
|
+
parent,
|
|
8540
|
+
id: `mnemon-run-v1-${randomUUID()}`
|
|
8541
|
+
}, operation);
|
|
8542
|
+
}
|
|
8543
|
+
function reservedChildId(parent) {
|
|
8544
|
+
const current = creatingChild.getStore();
|
|
8545
|
+
return current?.parent === parent && !current.claimed ? current.id : void 0;
|
|
8546
|
+
}
|
|
8547
|
+
function claimReservedChild(parent) {
|
|
8548
|
+
const current = creatingChild.getStore();
|
|
8549
|
+
if (current?.parent !== parent || current.claimed) return void 0;
|
|
8550
|
+
current.claimed = true;
|
|
8551
|
+
return current.id;
|
|
8552
|
+
}
|
|
8553
|
+
function isCreatingChild(agents, child) {
|
|
8554
|
+
const current = creatingChild.getStore();
|
|
8555
|
+
return current !== void 0 && isReservedChild(agents, current.parent, child);
|
|
8556
|
+
}
|
|
8557
|
+
/** A lineage string or same-tree relationship is not a creation capability. */
|
|
8558
|
+
function isReservedChild(agents, parent, child) {
|
|
8559
|
+
const current = creatingChild.getStore();
|
|
8560
|
+
if (current?.parent !== parent || !current.claimed || current.id !== child.id || agents?.get?.(parent.id) !== parent || agents.get(child.id) !== child || current.child !== void 0 && current.child !== child) return false;
|
|
8561
|
+
current.child = child;
|
|
8562
|
+
return true;
|
|
8563
|
+
}
|
|
8564
|
+
//#endregion
|
|
8464
8565
|
//#region src/host/review-tools.ts
|
|
8465
8566
|
const startingChild$1 = new AsyncLocalStorage();
|
|
8466
8567
|
/**
|
|
@@ -8470,7 +8571,11 @@ const startingChild$1 = new AsyncLocalStorage();
|
|
|
8470
8571
|
* still requires startGuardedReview; it never removes another plugin's policy.
|
|
8471
8572
|
*/
|
|
8472
8573
|
function idleReviewBlockReason(parent, agentTeams = "pause") {
|
|
8473
|
-
|
|
8574
|
+
if (agentTeams === "scoped") return void 0;
|
|
8575
|
+
const teams = parent.ctx?.get?.("agentTeams");
|
|
8576
|
+
if (teams === void 0) return void 0;
|
|
8577
|
+
const tools = parent.ctx.tools;
|
|
8578
|
+
return tools?.get?.("spawn_teammate", parent) !== void 0 || typeof teams?.isEnabled === "function" && teams.isEnabled(parent) && tools?.get?.("team_task_create", parent) !== void 0 ? "agent-team" : void 0;
|
|
8474
8579
|
}
|
|
8475
8580
|
/** The two writes an idle review can make, one per layer; both are rows of the tool table. */
|
|
8476
8581
|
const DOCUMENT_WRITE = "mnemon_document_create";
|
|
@@ -8522,20 +8627,24 @@ function startGuardedReview(host, parent, toolNames, start, published, policy) {
|
|
|
8522
8627
|
async function startGuardedChild(host, parent, start, guard, options) {
|
|
8523
8628
|
const { purpose, published } = options;
|
|
8524
8629
|
const agents = host.agents;
|
|
8525
|
-
|
|
8630
|
+
const reserved = activationOwned(host.subagents);
|
|
8631
|
+
if (reserved && typeof agents?.get !== "function") throw new Error(`Mnemon ${purpose} requires exact live Agent lookup`);
|
|
8632
|
+
if (!reserved && typeof agents?.isOwnedBy !== "function") {
|
|
8526
8633
|
if (options.optional === true) return start();
|
|
8527
8634
|
throw new Error(`Mnemon ${purpose} requires DSH Agent ownership and scoped tool guard support`);
|
|
8528
8635
|
}
|
|
8529
8636
|
const pending = Symbol(`Mnemon ${purpose} publication`);
|
|
8530
8637
|
const guards = /* @__PURE__ */ new Map();
|
|
8531
8638
|
let attachmentError;
|
|
8639
|
+
let expectedChildId;
|
|
8532
8640
|
let run;
|
|
8533
8641
|
const listener = host.on("agent/created", (({ agent }) => {
|
|
8534
|
-
if (startingChild$1.getStore() !== pending
|
|
8642
|
+
if (startingChild$1.getStore() !== pending) return;
|
|
8643
|
+
if (reserved ? agent.id !== expectedChildId || !isReservedChild(agents, parent, agent) : !agents.isOwnedBy(agent.id, parent)) return;
|
|
8535
8644
|
try {
|
|
8536
8645
|
const tools = agent.ctx?.tools;
|
|
8537
8646
|
if (typeof tools?.guard !== "function") {
|
|
8538
|
-
if (options.optional === true) return;
|
|
8647
|
+
if (options.optional === true && !reserved) return;
|
|
8539
8648
|
throw new Error(`Mnemon ${purpose} requires DSH scoped tool guard support`);
|
|
8540
8649
|
}
|
|
8541
8650
|
const dispose = tools.guard(guard);
|
|
@@ -8563,13 +8672,16 @@ async function startGuardedChild(host, parent, start, guard, options) {
|
|
|
8563
8672
|
if (failed.length) throw new AggregateError(failed.map((outcome) => outcome.reason), `Mnemon ${purpose} guard cleanup failed`);
|
|
8564
8673
|
};
|
|
8565
8674
|
try {
|
|
8566
|
-
run = await startingChild$1.run(pending,
|
|
8675
|
+
run = await startingChild$1.run(pending, () => reserved ? withReservedChild(parent, () => {
|
|
8676
|
+
expectedChildId = reservedChildId(parent);
|
|
8677
|
+
return start();
|
|
8678
|
+
}, true) : start());
|
|
8567
8679
|
if (attachmentError !== void 0) throw attachmentError;
|
|
8568
8680
|
const runId = run.id;
|
|
8569
8681
|
const child = run.localAgent ?? [...guards.keys()].find((agent) => agent.id === runId);
|
|
8570
8682
|
if (child === void 0 || !guards.has(child)) {
|
|
8571
|
-
const local = run.localAgent ?? agents
|
|
8572
|
-
if (options.optional === true && guards.size === 0 && typeof local?.ctx?.tools?.guard !== "function") {
|
|
8683
|
+
const local = run.localAgent ?? agents?.get?.(runId);
|
|
8684
|
+
if (!reserved && options.optional === true && guards.size === 0 && typeof local?.ctx?.tools?.guard !== "function") {
|
|
8573
8685
|
await closeObserver();
|
|
8574
8686
|
return run;
|
|
8575
8687
|
}
|
|
@@ -8604,13 +8716,13 @@ async function startGuardedChild(host, parent, start, guard, options) {
|
|
|
8604
8716
|
}
|
|
8605
8717
|
//#endregion
|
|
8606
8718
|
//#region src/host/review-checkpoint.ts
|
|
8607
|
-
function record$
|
|
8719
|
+
function record$3(value) {
|
|
8608
8720
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
8609
8721
|
}
|
|
8610
8722
|
function textContent(content, toolResult = false) {
|
|
8611
8723
|
if (!Array.isArray(content)) return "";
|
|
8612
8724
|
return content.map((block) => {
|
|
8613
|
-
const value = record$
|
|
8725
|
+
const value = record$3(block);
|
|
8614
8726
|
if (value.type === "text" && typeof value.text === "string") return value.text;
|
|
8615
8727
|
if (toolResult && value.type === "tool-result" && value.isError !== true) return textContent(value.content);
|
|
8616
8728
|
return "";
|
|
@@ -8634,8 +8746,8 @@ function reviewCheckpoint(session, maxChars) {
|
|
|
8634
8746
|
"assistant/message",
|
|
8635
8747
|
"tool/result"
|
|
8636
8748
|
].includes(event.type)) continue;
|
|
8637
|
-
const message = record$
|
|
8638
|
-
const source = record$
|
|
8749
|
+
const message = record$3(event.type === "user/message" ? event.data : event.data.message);
|
|
8750
|
+
const source = record$3(message.source);
|
|
8639
8751
|
if (event.type === "user/message" && source.kind !== "user") continue;
|
|
8640
8752
|
const text = textContent(message.content, event.type === "tool/result");
|
|
8641
8753
|
if (!text) continue;
|
|
@@ -8649,8 +8761,14 @@ function reviewCheckpoint(session, maxChars) {
|
|
|
8649
8761
|
//#endregion
|
|
8650
8762
|
//#region src/host/plugin-message.ts
|
|
8651
8763
|
const MNEMON_PLUGIN_SOURCE = "dsh-mnemon";
|
|
8764
|
+
/** Includes the published V3 wrapper and its official V4 migration. */
|
|
8765
|
+
function isMnemonMessageSource(source) {
|
|
8766
|
+
if (typeof source !== "object" || source === null) return false;
|
|
8767
|
+
const { kind, plugin } = source;
|
|
8768
|
+
return kind === "dsh-mnemon" || kind === `plugin:dsh-mnemon` || kind === "plugin" && plugin === "dsh-mnemon";
|
|
8769
|
+
}
|
|
8652
8770
|
/** A user-role message this plugin adds to a step, marked with its own source. */
|
|
8653
|
-
function createPluginMessage(text, form) {
|
|
8771
|
+
function createPluginMessage(text, form, snapshot) {
|
|
8654
8772
|
return structuredClone({
|
|
8655
8773
|
id: randomUUID(),
|
|
8656
8774
|
role: "user",
|
|
@@ -8660,7 +8778,8 @@ function createPluginMessage(text, form) {
|
|
|
8660
8778
|
}],
|
|
8661
8779
|
source: {
|
|
8662
8780
|
kind: MNEMON_PLUGIN_SOURCE,
|
|
8663
|
-
form
|
|
8781
|
+
form,
|
|
8782
|
+
...snapshot === void 0 ? {} : { snapshot }
|
|
8664
8783
|
}
|
|
8665
8784
|
});
|
|
8666
8785
|
}
|
|
@@ -8719,10 +8838,13 @@ function continueAfterRefusal(agent) {
|
|
|
8719
8838
|
* that does not report creation leaves the child as it was.
|
|
8720
8839
|
*/
|
|
8721
8840
|
async function startWithContinuationTurns(host, parent, start) {
|
|
8841
|
+
const reserved = activationOwned(host.subagents);
|
|
8722
8842
|
const pending = Symbol("Mnemon delegated start");
|
|
8723
8843
|
const stops = [];
|
|
8844
|
+
let expectedChildId;
|
|
8724
8845
|
const listener = host.on("agent/created", (({ agent }) => {
|
|
8725
8846
|
if (startingChild.getStore() !== pending) return;
|
|
8847
|
+
if (reserved && (agent.id !== expectedChildId || !isReservedChild(host.agents, parent, agent))) return;
|
|
8726
8848
|
if (typeof host.agents?.isOwnedBy === "function" && !host.agents.isOwnedBy(agent.id, parent)) return;
|
|
8727
8849
|
for (const stop of continueAfterRefusal(agent)) if (typeof stop === "function") stops.push(stop);
|
|
8728
8850
|
}));
|
|
@@ -8731,7 +8853,10 @@ async function startWithContinuationTurns(host, parent, start) {
|
|
|
8731
8853
|
};
|
|
8732
8854
|
let run;
|
|
8733
8855
|
try {
|
|
8734
|
-
run = await startingChild.run(pending,
|
|
8856
|
+
run = await startingChild.run(pending, () => reserved ? withReservedChild(parent, () => {
|
|
8857
|
+
expectedChildId = reservedChildId(parent);
|
|
8858
|
+
return start();
|
|
8859
|
+
}) : start());
|
|
8735
8860
|
} catch (error) {
|
|
8736
8861
|
await release();
|
|
8737
8862
|
throw error;
|
|
@@ -8790,6 +8915,7 @@ function childComposition(subagents, provider) {
|
|
|
8790
8915
|
* step, which needs `publications`.
|
|
8791
8916
|
*/
|
|
8792
8917
|
async function startSubagent(subagents, agents, provider, request, publications) {
|
|
8918
|
+
if (typeof subagents.startActivation === "function" && reservedChildId(request.parent) === void 0) return withReservedChild(request.parent, () => startSubagent(subagents, agents, provider, request, publications));
|
|
8793
8919
|
const { toolFilter, persona, ...task } = request;
|
|
8794
8920
|
if (toolFilter === void 0 && persona === void 0 || childComposition(subagents, subagents.getProvider(provider)) !== "publication") return startOnHost(subagents, agents, provider, request);
|
|
8795
8921
|
if (publications === void 0) throw new Error("This DSH takes no subagent persona or tool filter at start, and dsh-mnemon cannot observe the child it publishes");
|
|
@@ -8797,14 +8923,26 @@ async function startSubagent(subagents, agents, provider, request, publications)
|
|
|
8797
8923
|
...toolFilter === void 0 ? {} : { toolFilter },
|
|
8798
8924
|
...persona === void 0 ? {} : { persona }
|
|
8799
8925
|
};
|
|
8800
|
-
return startComposed(publications, agents, request.parent, composition, () => startOnHost(subagents, agents, provider, task));
|
|
8926
|
+
return startComposed(publications, agents, request.parent, composition, () => startOnHost(subagents, agents, provider, task), activationOwned(subagents));
|
|
8801
8927
|
}
|
|
8802
8928
|
async function startOnHost(subagents, agents, provider, request) {
|
|
8803
8929
|
if (typeof subagents.startActivation === "function") {
|
|
8804
8930
|
const { label, signal, ...task } = request;
|
|
8805
|
-
const
|
|
8931
|
+
const modern = activationOwned(subagents);
|
|
8932
|
+
const childId = claimReservedChild(request.parent);
|
|
8933
|
+
if (modern && (childId === void 0 || agents?.get?.(request.parent.id) !== request.parent)) throw new Error("Mnemon requires an exact live parent and reserved child identity on an activation-owned DSH");
|
|
8934
|
+
const startCurrent = subagents.startActivation;
|
|
8935
|
+
const activation = modern ? await startCurrent.call(subagents, {
|
|
8936
|
+
...task,
|
|
8806
8937
|
provider,
|
|
8807
8938
|
label,
|
|
8939
|
+
signal,
|
|
8940
|
+
childId,
|
|
8941
|
+
delivery: "caller"
|
|
8942
|
+
}) : await subagents.startActivation({
|
|
8943
|
+
provider,
|
|
8944
|
+
label,
|
|
8945
|
+
...childId === void 0 ? {} : { childId },
|
|
8808
8946
|
request: task,
|
|
8809
8947
|
signal,
|
|
8810
8948
|
delivery: "caller"
|
|
@@ -8818,7 +8956,9 @@ async function startOnHost(subagents, agents, provider, request) {
|
|
|
8818
8956
|
activation.result.then(release, release);
|
|
8819
8957
|
if (signal.aborted) stop();
|
|
8820
8958
|
const child = agents?.get?.(activation.childId);
|
|
8821
|
-
|
|
8959
|
+
if (modern && (activation.childId !== childId || child === void 0 || !isReservedChild(agents, request.parent, child))) throw new Error("DSH did not return the reserved live Mnemon child");
|
|
8960
|
+
if (modern && child !== void 0) titleTask(child, label.startsWith("Mnemon ") ? label : `Mnemon · ${label}`);
|
|
8961
|
+
const localAgent = child !== void 0 && (modern || typeof agents?.isOwnedBy !== "function" || agents.isOwnedBy(child.id, request.parent)) ? child : void 0;
|
|
8822
8962
|
return {
|
|
8823
8963
|
id: activation.childId,
|
|
8824
8964
|
...localAgent === void 0 ? {} : { localAgent },
|
|
@@ -8858,12 +8998,14 @@ function compose(agent, { toolFilter, persona }) {
|
|
|
8858
8998
|
* check confirms the parent. A failed composition makes DSH roll the child
|
|
8859
8999
|
* back; a child that was not seen published is disposed and refused.
|
|
8860
9000
|
*/
|
|
8861
|
-
async function startComposed(publications, agents, parent, composition, start) {
|
|
9001
|
+
async function startComposed(publications, agents, parent, composition, start, reserved = false) {
|
|
8862
9002
|
const pending = Symbol("Mnemon child composition");
|
|
9003
|
+
const expectedChildId = reserved ? reservedChildId(parent) : void 0;
|
|
8863
9004
|
const composed = /* @__PURE__ */ new Set();
|
|
8864
9005
|
let failure;
|
|
8865
9006
|
const listener = publications.on("agent/created", (({ agent }) => {
|
|
8866
9007
|
if (composingChild.getStore() !== pending) return;
|
|
9008
|
+
if (reserved && (agent.id !== expectedChildId || !isReservedChild(agents, parent, agent))) return;
|
|
8867
9009
|
if (typeof agents?.isOwnedBy === "function" && !agents.isOwnedBy(agent.id, parent)) return;
|
|
8868
9010
|
try {
|
|
8869
9011
|
compose(agent, composition);
|
|
@@ -10332,6 +10474,7 @@ var AgentMemoryTurn = class {
|
|
|
10332
10474
|
agent;
|
|
10333
10475
|
runtime;
|
|
10334
10476
|
delegation;
|
|
10477
|
+
independent;
|
|
10335
10478
|
pinned;
|
|
10336
10479
|
/** A turn that runs without memory because no composition was serving when it began. */
|
|
10337
10480
|
unavailable;
|
|
@@ -10342,10 +10485,11 @@ var AgentMemoryTurn = class {
|
|
|
10342
10485
|
lastInspection;
|
|
10343
10486
|
lastWorkspace;
|
|
10344
10487
|
releaseDelegation;
|
|
10345
|
-
constructor(agent, runtime, delegation) {
|
|
10488
|
+
constructor(agent, runtime, delegation, independent = false) {
|
|
10346
10489
|
this.agent = agent;
|
|
10347
10490
|
this.runtime = runtime;
|
|
10348
10491
|
this.delegation = delegation;
|
|
10492
|
+
this.independent = independent;
|
|
10349
10493
|
if (delegation === void 0) return;
|
|
10350
10494
|
this.releaseDelegation = runtime.executions.retain(agent, delegation);
|
|
10351
10495
|
}
|
|
@@ -10437,7 +10581,7 @@ var AgentMemoryTurn = class {
|
|
|
10437
10581
|
}
|
|
10438
10582
|
}
|
|
10439
10583
|
async pin(turn, generation, signal) {
|
|
10440
|
-
const execution = await this.runtime.executions.turn(this.agent, turn, signal, this.delegation);
|
|
10584
|
+
const execution = await this.runtime.executions.turn(this.agent, turn, signal, this.delegation, this.independent);
|
|
10441
10585
|
const { graph, context } = execution;
|
|
10442
10586
|
try {
|
|
10443
10587
|
if (this.closed || this.generation !== generation) throw new Error("memory turn ended during View preparation");
|
|
@@ -10581,6 +10725,136 @@ var AutomaticReviewAdmission = class {
|
|
|
10581
10725
|
}
|
|
10582
10726
|
};
|
|
10583
10727
|
//#endregion
|
|
10728
|
+
//#region src/host/subagent-errors.ts
|
|
10729
|
+
/** A provider stop, kept separate from schema, startup and cancellation errors. */
|
|
10730
|
+
var SubagentStoppedError = class extends Error {
|
|
10731
|
+
stopReason;
|
|
10732
|
+
maxTokens;
|
|
10733
|
+
constructor(stopReason, maxTokens, detail) {
|
|
10734
|
+
super(`memory subagent stopped with ${stopReason}${detail === void 0 ? "" : `: ${detail}`}`);
|
|
10735
|
+
this.stopReason = stopReason;
|
|
10736
|
+
this.maxTokens = maxTokens;
|
|
10737
|
+
this.name = "SubagentStoppedError";
|
|
10738
|
+
}
|
|
10739
|
+
};
|
|
10740
|
+
//#endregion
|
|
10741
|
+
//#region src/host/task-continuation.ts
|
|
10742
|
+
const FINISHED_TASK_REFUSAL = "This Mnemon task has finished. Send a new user message in this conversation to continue working with memory.";
|
|
10743
|
+
const CONTINUATION_GUIDANCE = "This is a new user request in a completed Mnemon task conversation. The previous task stays completed. Use the current memory View and ordinary memory tools for this request; require receipts for changes. Do not repeat the previous task or submit its expired completion request.";
|
|
10744
|
+
/** Older workers have no reserved ID: require their own Host descriptor and task contract. */
|
|
10745
|
+
function isLegacyMemoryTask(agent) {
|
|
10746
|
+
if (agent.session.header?.origin !== "subagent") return false;
|
|
10747
|
+
const events = agent.session.snapshotEvents(agent.session.inheritedEventCount ?? 0);
|
|
10748
|
+
const descriptor = events.findLastIndex((event) => event.type === "subagent/descriptor");
|
|
10749
|
+
if (descriptor < 0 || typeof events[descriptor].data.label !== "string" || !events[descriptor].data.label.startsWith("Mnemon ")) return false;
|
|
10750
|
+
const own = events.slice(descriptor + 1);
|
|
10751
|
+
const textOf = (value) => {
|
|
10752
|
+
return value?.content?.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n") ?? "";
|
|
10753
|
+
};
|
|
10754
|
+
const prompt = own.find((event) => event.type === "user/message" && (event.data.message ?? event.data).source?.kind === "user");
|
|
10755
|
+
if (prompt === void 0 || !/^\[dsh-mnemon:delegated [a-z-]+\](?:\n|$)/u.test(textOf(prompt.data.message ?? prompt.data))) return false;
|
|
10756
|
+
return own.some((event) => event.type === "system/message" && textOf(event.data.message).includes("Completion protocol: call `mnemon_subagent_result` exactly once with requestId `"));
|
|
10757
|
+
}
|
|
10758
|
+
/** A resumed task receives memory authority only for a newly accepted human turn. */
|
|
10759
|
+
var TaskContinuation = class {
|
|
10760
|
+
agent;
|
|
10761
|
+
runtime;
|
|
10762
|
+
liveAgent;
|
|
10763
|
+
memory;
|
|
10764
|
+
authorizedTurn;
|
|
10765
|
+
stops = [];
|
|
10766
|
+
views = /* @__PURE__ */ new Map();
|
|
10767
|
+
closed = false;
|
|
10768
|
+
constructor(agent, runtime, liveAgent) {
|
|
10769
|
+
this.agent = agent;
|
|
10770
|
+
this.runtime = runtime;
|
|
10771
|
+
this.liveAgent = liveAgent;
|
|
10772
|
+
this.memory = new AgentMemoryTurn(agent, runtime, void 0, true);
|
|
10773
|
+
}
|
|
10774
|
+
start() {
|
|
10775
|
+
try {
|
|
10776
|
+
const guard = this.agent.ctx.tools?.guard?.((execution) => this.guard(execution));
|
|
10777
|
+
if (typeof guard !== "function") throw new Error("Task continuation requires a scoped memory tool guard");
|
|
10778
|
+
this.stops.push(guard);
|
|
10779
|
+
this.stops.push(this.agent.ctx.on("agent/pre-step", ((step, next) => this.preStep(step, next)), { prepend: true }));
|
|
10780
|
+
this.stops.push(this.agent.ctx.on("system-prompt/assemble", (async (_assembly, context, next) => {
|
|
10781
|
+
const assembly = await next();
|
|
10782
|
+
if (context.agent !== void 0 && context.agent !== this.agent) return assembly;
|
|
10783
|
+
const pin = this.memory.current;
|
|
10784
|
+
return applyMemoryViewGuidance(assembly, pin?.context.view, pin?.graph.config.routingGuidance);
|
|
10785
|
+
})));
|
|
10786
|
+
this.stops.push(this.agent.ctx.on("session/event", ((session, event) => {
|
|
10787
|
+
if (session === this.agent.session && event.type === "turn/end" && event.data.turn === this.authorizedTurn) this.end();
|
|
10788
|
+
})));
|
|
10789
|
+
return () => this.dispose();
|
|
10790
|
+
} catch (error) {
|
|
10791
|
+
this.dispose();
|
|
10792
|
+
throw error;
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
ownsHumanTurn(actor) {
|
|
10796
|
+
return !this.closed && actor === this.agent && this.liveAgent(actor.id) === actor && this.authorizedTurn !== void 0 && this.memory.current?.turn === this.authorizedTurn && openAgentTurn(actor) === this.authorizedTurn;
|
|
10797
|
+
}
|
|
10798
|
+
guard(execution) {
|
|
10799
|
+
if (!execution.name?.startsWith("mnemon_")) return void 0;
|
|
10800
|
+
const actor = execution.agent;
|
|
10801
|
+
if (this.closed || actor === void 0 || this.liveAgent(actor.id) !== actor) return FINISHED_TASK_REFUSAL;
|
|
10802
|
+
if (actor !== this.agent) {
|
|
10803
|
+
try {
|
|
10804
|
+
const graph = this.runtime.forAgent(actor);
|
|
10805
|
+
const pin = graph.composableTurns.activeTurn(actor.id);
|
|
10806
|
+
if (pin !== void 0 && this.views.get(pin.view.id) === graph) return void 0;
|
|
10807
|
+
} catch {}
|
|
10808
|
+
return FINISHED_TASK_REFUSAL;
|
|
10809
|
+
}
|
|
10810
|
+
if (this.authorizedTurn === void 0) return FINISHED_TASK_REFUSAL;
|
|
10811
|
+
if (execution.name === "mnemon_subagent_result") return "The previous Mnemon task result is already settled. Use ordinary memory tools for the new user request.";
|
|
10812
|
+
if (this.memory.current?.turn !== this.authorizedTurn) return this.memory.unavailableReason ?? "Memory is unavailable for this request.";
|
|
10813
|
+
}
|
|
10814
|
+
async preStep(step, next) {
|
|
10815
|
+
const decision = await next();
|
|
10816
|
+
if (step.agent !== this.agent) return decision;
|
|
10817
|
+
if (decision.kind === "reject" || step.signal.aborted || this.closed || this.liveAgent(this.agent.id) !== this.agent) {
|
|
10818
|
+
this.end();
|
|
10819
|
+
return decision;
|
|
10820
|
+
}
|
|
10821
|
+
const starting = this.authorizedTurn !== step.turn;
|
|
10822
|
+
if (starting) this.end();
|
|
10823
|
+
if (decision.messages.some((message) => message.source.kind === "user" && message.content.some((block) => block.type !== "text" || (block.text?.trim().length ?? 0) > 0))) this.authorizedTurn = step.turn;
|
|
10824
|
+
if (this.authorizedTurn !== step.turn || openAgentTurn(this.agent) !== step.turn) return decision;
|
|
10825
|
+
try {
|
|
10826
|
+
await this.memory.begin(step.turn, step.signal);
|
|
10827
|
+
} catch (error) {
|
|
10828
|
+
this.end();
|
|
10829
|
+
throw error;
|
|
10830
|
+
}
|
|
10831
|
+
const pin = this.memory.current;
|
|
10832
|
+
for (const [id, graph] of this.views) if (graph.composableTurns.get(id) === void 0) this.views.delete(id);
|
|
10833
|
+
if (pin !== void 0) this.views.set(pin.context.view.id, pin.graph);
|
|
10834
|
+
if (!starting && step.step !== 1) return decision;
|
|
10835
|
+
const wake = pin === void 0 ? "" : modelMemoryWake(pin.graph, pin.context).text;
|
|
10836
|
+
return {
|
|
10837
|
+
...decision,
|
|
10838
|
+
messages: [...decision.messages, createPluginMessage(`${CONTINUATION_GUIDANCE}\n\n${wake}`, "instructions")]
|
|
10839
|
+
};
|
|
10840
|
+
}
|
|
10841
|
+
end() {
|
|
10842
|
+
this.authorizedTurn = void 0;
|
|
10843
|
+
this.memory.end();
|
|
10844
|
+
}
|
|
10845
|
+
dispose() {
|
|
10846
|
+
if (this.closed) return;
|
|
10847
|
+
this.closed = true;
|
|
10848
|
+
try {
|
|
10849
|
+
for (const stop of this.stops.splice(0).reverse()) stop();
|
|
10850
|
+
} finally {
|
|
10851
|
+
this.authorizedTurn = void 0;
|
|
10852
|
+
this.memory.dispose();
|
|
10853
|
+
this.views.clear();
|
|
10854
|
+
}
|
|
10855
|
+
}
|
|
10856
|
+
};
|
|
10857
|
+
//#endregion
|
|
10584
10858
|
//#region src/host/subagent.ts
|
|
10585
10859
|
function evidenceInsights(evidence) {
|
|
10586
10860
|
return evidence.items.map((item) => {
|
|
@@ -10626,7 +10900,6 @@ const REVIEW_TOOLS = [
|
|
|
10626
10900
|
const RESULT_TOOL_NAME = "mnemon_subagent_result";
|
|
10627
10901
|
/** How many finished delegated children stay refused memory tools if DSH resumes one. */
|
|
10628
10902
|
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
10903
|
const RESULT_TOOL_INPUT_SCHEMA = {
|
|
10631
10904
|
type: "object",
|
|
10632
10905
|
properties: {
|
|
@@ -10763,8 +11036,13 @@ var MnemonSubagentCoordinator = class {
|
|
|
10763
11036
|
resultRequests = /* @__PURE__ */ new Map();
|
|
10764
11037
|
disposeResultTool;
|
|
10765
11038
|
disposeResumeGuard;
|
|
11039
|
+
disposeTaskNotices;
|
|
10766
11040
|
/** Delegated children whose run has ended, newest last, at most FINISHED_CHILDREN. */
|
|
10767
11041
|
finishedChildren = /* @__PURE__ */ new Set();
|
|
11042
|
+
continuations = /* @__PURE__ */ new Map();
|
|
11043
|
+
continuationOwners = /* @__PURE__ */ new WeakMap();
|
|
11044
|
+
providerNames = /* @__PURE__ */ new Set(["spawn", "fork"]);
|
|
11045
|
+
providerObservers = [];
|
|
10768
11046
|
disposed = false;
|
|
10769
11047
|
reviewAdmission = new AutomaticReviewAdmission();
|
|
10770
11048
|
constructor(subagents, runtimeSource, resultRuntime, taskAgentModelResolver, runtimeMaintenanceMaxTokensResolver, runtimeMaintenanceTaskRunner) {
|
|
@@ -10799,11 +11077,39 @@ var MnemonSubagentCoordinator = class {
|
|
|
10799
11077
|
});
|
|
10800
11078
|
if (typeof registration !== "function") throw new Error("dsh-mnemon subagent result tool registration did not return a disposer");
|
|
10801
11079
|
this.disposeResultTool = registration;
|
|
11080
|
+
const notices = resultRuntime.on("agent/pre-step", (async (step, next) => {
|
|
11081
|
+
const ignored = new Set(step.messages.filter((message) => {
|
|
11082
|
+
const source = message.source;
|
|
11083
|
+
return source.kind === "subagent-settled" && typeof source.senderSessionId === "string" && (isTaskRecord(source.senderSessionId) || this.finishedChildren.has(source.senderSessionId));
|
|
11084
|
+
}).map((message) => message.id));
|
|
11085
|
+
if (ignored.size === 0) return next();
|
|
11086
|
+
if (step.step === 1 && ignored.size === step.messages.length) return {
|
|
11087
|
+
kind: "enter",
|
|
11088
|
+
messages: []
|
|
11089
|
+
};
|
|
11090
|
+
const decision = await next();
|
|
11091
|
+
return decision.kind === "reject" ? decision : {
|
|
11092
|
+
...decision,
|
|
11093
|
+
messages: decision.messages.filter((message) => !ignored.has(message.id))
|
|
11094
|
+
};
|
|
11095
|
+
}), { prepend: true });
|
|
11096
|
+
if (typeof notices === "function") this.disposeTaskNotices = notices;
|
|
11097
|
+
if (typeof subagents.list !== "function") {
|
|
11098
|
+
for (const stop of [resultRuntime.on("subagent/provider-added", ((provider) => {
|
|
11099
|
+
this.providerNames.add(provider.name);
|
|
11100
|
+
})), resultRuntime.on("subagent/provider-removed", ((name) => {
|
|
11101
|
+
this.providerNames.delete(name);
|
|
11102
|
+
}))]) if (typeof stop === "function") this.providerObservers.push(stop);
|
|
11103
|
+
}
|
|
10802
11104
|
}
|
|
10803
11105
|
dispose() {
|
|
10804
11106
|
this.disposed = true;
|
|
10805
11107
|
this.reviewAdmission.dispose();
|
|
10806
11108
|
this.resultRequests.clear();
|
|
11109
|
+
this.disposeTaskNotices?.();
|
|
11110
|
+
this.disposeTaskNotices = void 0;
|
|
11111
|
+
for (const stop of [...this.continuations.values()].reverse()) stop();
|
|
11112
|
+
for (const stop of this.providerObservers.splice(0)) stop();
|
|
10807
11113
|
const resumeGuard = this.disposeResumeGuard;
|
|
10808
11114
|
this.disposeResumeGuard = void 0;
|
|
10809
11115
|
resumeGuard?.();
|
|
@@ -10814,10 +11120,68 @@ var MnemonSubagentCoordinator = class {
|
|
|
10814
11120
|
snapshot() {
|
|
10815
11121
|
return { ...this.counters };
|
|
10816
11122
|
}
|
|
11123
|
+
isFinishedTask(id, agent) {
|
|
11124
|
+
if (this.finishedChildren.has(id)) return true;
|
|
11125
|
+
if (agent === void 0 || agent.id !== id || !isLegacyMemoryTask(agent)) return false;
|
|
11126
|
+
this.finished(id);
|
|
11127
|
+
return true;
|
|
11128
|
+
}
|
|
11129
|
+
isUserContinuation(agent) {
|
|
11130
|
+
return this.continuationOwners.get(agent)?.ownsHumanTurn(agent) === true;
|
|
11131
|
+
}
|
|
11132
|
+
delegationLimit(parent) {
|
|
11133
|
+
if (!this.isUserContinuation(parent)) return 1;
|
|
11134
|
+
const depths = [parent.session.header?.delegationDepth ?? 0, parent.options?.subagentDepth ?? 0];
|
|
11135
|
+
if (depths.some((depth) => !Number.isSafeInteger(depth) || depth < 0 || Object.is(depth, -0) || depth === Number.MAX_SAFE_INTEGER)) throw new Error("Cannot determine a safe delegation depth for this continued task");
|
|
11136
|
+
return Math.max(...depths) + 1;
|
|
11137
|
+
}
|
|
11138
|
+
/** User continuation is a fresh scoped turn, never revival of a delegated run. */
|
|
11139
|
+
continueTask(agent) {
|
|
11140
|
+
if (this.continuations.has(agent) || this.disposed) return;
|
|
11141
|
+
if (typeof agent.ctx.effect !== "function" || this.resultRuntime?.agents?.get === void 0) {
|
|
11142
|
+
agent.ctx.tools?.guard?.((execution) => execution.name?.startsWith("mnemon_") ? FINISHED_TASK_REFUSAL : void 0);
|
|
11143
|
+
return;
|
|
11144
|
+
}
|
|
11145
|
+
const continuation = new TaskContinuation(agent, this.runtimeSource, (id) => this.resultRuntime?.agents?.get?.(id));
|
|
11146
|
+
let dispose;
|
|
11147
|
+
dispose = agent.ctx.effect(() => {
|
|
11148
|
+
const stop = continuation.start();
|
|
11149
|
+
this.continuationOwners.set(agent, continuation);
|
|
11150
|
+
return () => {
|
|
11151
|
+
try {
|
|
11152
|
+
stop();
|
|
11153
|
+
} finally {
|
|
11154
|
+
if (this.continuations.get(agent) === dispose) this.continuations.delete(agent);
|
|
11155
|
+
this.continuationOwners.delete(agent);
|
|
11156
|
+
}
|
|
11157
|
+
};
|
|
11158
|
+
}, "dsh-mnemon.task-continuation()");
|
|
11159
|
+
this.continuations.set(agent, dispose);
|
|
11160
|
+
}
|
|
11161
|
+
/** Spawn may use an independent tool scope, so transfer the turn guard explicitly. */
|
|
11162
|
+
inheritTaskContinuation(parent, child) {
|
|
11163
|
+
const owner = this.continuationOwners.get(parent);
|
|
11164
|
+
if (owner === void 0 || this.continuations.has(child)) return;
|
|
11165
|
+
let dispose;
|
|
11166
|
+
dispose = child.ctx.effect(() => {
|
|
11167
|
+
const stop = child.ctx.tools?.guard?.((execution) => owner.guard(execution));
|
|
11168
|
+
if (typeof stop !== "function") throw new Error("A continued task child requires a scoped memory tool guard");
|
|
11169
|
+
this.continuationOwners.set(child, owner);
|
|
11170
|
+
return () => {
|
|
11171
|
+
try {
|
|
11172
|
+
stop();
|
|
11173
|
+
} finally {
|
|
11174
|
+
if (this.continuations.get(child) === dispose) this.continuations.delete(child);
|
|
11175
|
+
this.continuationOwners.delete(child);
|
|
11176
|
+
}
|
|
11177
|
+
};
|
|
11178
|
+
}, "dsh-mnemon.task-delegation()");
|
|
11179
|
+
this.continuations.set(child, dispose);
|
|
11180
|
+
}
|
|
10817
11181
|
/**
|
|
10818
11182
|
* DSH 0.2.1 can resume a finished child from its saved state when another Agent sends it a
|
|
10819
|
-
* message: with its persona and tools, but without the guard its run held.
|
|
10820
|
-
*
|
|
11183
|
+
* message: with its persona and tools, but without the guard its run held. Only a fresh
|
|
11184
|
+
* human turn may acquire new authority; automatic continuations remain denied (#387).
|
|
10821
11185
|
*/
|
|
10822
11186
|
finished(id) {
|
|
10823
11187
|
this.finishedChildren.delete(id);
|
|
@@ -10826,9 +11190,7 @@ var MnemonSubagentCoordinator = class {
|
|
|
10826
11190
|
if (this.disposeResumeGuard !== void 0 || this.disposed || this.resultRuntime === void 0) return;
|
|
10827
11191
|
const resumed = this.resultRuntime.on("agent/created", (({ agent }) => {
|
|
10828
11192
|
if (!this.finishedChildren.has(agent.id)) return;
|
|
10829
|
-
|
|
10830
|
-
agent.ctx?.tools?.guard?.((execution) => execution.name?.startsWith("mnemon_") === true ? FINISHED_CHILD_REFUSAL : void 0);
|
|
10831
|
-
} catch {}
|
|
11193
|
+
this.continueTask(agent);
|
|
10832
11194
|
}));
|
|
10833
11195
|
if (typeof resumed === "function") this.disposeResumeGuard = resumed;
|
|
10834
11196
|
}
|
|
@@ -11267,8 +11629,8 @@ var MnemonSubagentCoordinator = class {
|
|
|
11267
11629
|
* One tool-less structured run for skill evolution: a review, a draft, a
|
|
11268
11630
|
* backtest step or a case derivation. The child sees only the prompt.
|
|
11269
11631
|
*/
|
|
11270
|
-
async skillTask(parent, label, prompt, schema, persona, signal, route) {
|
|
11271
|
-
const { provider, runId, result } = await this.delegate(parent, "skill", label, prompt, [], schema, signal, "spawn", persona, void 0, void 0, route);
|
|
11632
|
+
async skillTask(parent, label, prompt, schema, persona, signal, route, maxTokens) {
|
|
11633
|
+
const { provider, runId, result } = await this.delegate(parent, "skill", label, prompt, [], schema, signal, "spawn", persona, void 0, void 0, route, void 0, maxTokens);
|
|
11272
11634
|
return {
|
|
11273
11635
|
provider,
|
|
11274
11636
|
runId,
|
|
@@ -11429,9 +11791,9 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
11429
11791
|
execution.release();
|
|
11430
11792
|
}
|
|
11431
11793
|
}
|
|
11432
|
-
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route, admission) {
|
|
11794
|
+
async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route, admission, skillMaxTokens) {
|
|
11433
11795
|
if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
|
|
11434
|
-
const provider = this.provider(preferredProvider);
|
|
11796
|
+
const provider = this.provider(preferredProvider, this.runtimeSource.forAgent(parent).config.subagentProviderNames);
|
|
11435
11797
|
assertDshOutputSchema(outputSchema);
|
|
11436
11798
|
if (this.resultRuntime === void 0) throw new Error("dsh-mnemon subagent result tool runtime is unavailable");
|
|
11437
11799
|
const resultToolName = RESULT_TOOL_NAME;
|
|
@@ -11537,7 +11899,7 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
|
|
|
11537
11899
|
Completion protocol: call \`${resultToolName}\` exactly once with requestId \`${requestId}\` and result matching this JSON schema:
|
|
11538
11900
|
${JSON.stringify(outputSchema)}
|
|
11539
11901
|
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.`;
|
|
11540
|
-
const perOpMaxTokens = operation === "migration" || operation === "compaction" || operation === "reconcile" ? this.runtimeMaintenanceMaxTokensResolver?.() ?? 8192 : operation === "document-archive" ? 8192 : operation === "metadata-maintenance" ? 4096 : operation === "review" ? this.runtimeSource.config.idleReview.maxTokens : operation === "skill" ? 16384 : void 0;
|
|
11902
|
+
const perOpMaxTokens = operation === "migration" || operation === "compaction" || operation === "reconcile" ? this.runtimeMaintenanceMaxTokensResolver?.() ?? 8192 : operation === "document-archive" ? 8192 : operation === "metadata-maintenance" ? 4096 : operation === "review" ? this.runtimeSource.config.idleReview.maxTokens : operation === "skill" ? skillMaxTokens ?? 16384 : void 0;
|
|
11541
11903
|
const fixed = route ?? this.taskAgentModelResolver?.();
|
|
11542
11904
|
const baseAgentOptions = perOpMaxTokens === void 0 ? void 0 : { maxTokens: perOpMaxTokens };
|
|
11543
11905
|
const resolvedAgentOptions = fixed === void 0 ? baseAgentOptions : {
|
|
@@ -11560,7 +11922,7 @@ This is the only completion channel for this run. Do not finish with a plain-tex
|
|
|
11560
11922
|
parent,
|
|
11561
11923
|
signal,
|
|
11562
11924
|
...resolvedAgentOptions === void 0 ? {} : { agentOptions: resolvedAgentOptions },
|
|
11563
|
-
maxDepth:
|
|
11925
|
+
maxDepth: this.delegationLimit(parent),
|
|
11564
11926
|
toolFilter: { allow: [...this.availableTools(parent, tools), resultToolName] },
|
|
11565
11927
|
persona: completionPersona
|
|
11566
11928
|
}, host);
|
|
@@ -11576,11 +11938,11 @@ This is the only completion channel for this run. Do not finish with a plain-tex
|
|
|
11576
11938
|
if (captured !== void 0 && captured.agentId !== activeRun.id) throw new Error("Mnemon subagent result was recorded by a different child");
|
|
11577
11939
|
let structured = captured?.value ?? result.structured;
|
|
11578
11940
|
if (structured === void 0 && result.stopReason === "completed") structured = recoverStructuredResult(recovery, committedReceipts.filter((receipt) => receipt.agentId === activeRun.id));
|
|
11579
|
-
if (structured !== void 0) assertDshOutputValue(outputSchema, structured);
|
|
11580
11941
|
if (result.stopReason !== "completed") {
|
|
11581
11942
|
const detail = subagentFailureDetail(activeRun, result);
|
|
11582
|
-
throw new
|
|
11943
|
+
throw new SubagentStoppedError(result.stopReason, perOpMaxTokens, detail);
|
|
11583
11944
|
}
|
|
11945
|
+
if (structured !== void 0) assertDshOutputValue(outputSchema, structured);
|
|
11584
11946
|
if (structured === void 0) throw new Error("memory subagent completed without recording its result");
|
|
11585
11947
|
if (operation === "review" && [
|
|
11586
11948
|
"failed",
|
|
@@ -11644,8 +12006,12 @@ This is the only completion channel for this run. Do not finish with a plain-tex
|
|
|
11644
12006
|
this.counters.lastAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11645
12007
|
return completed;
|
|
11646
12008
|
}
|
|
11647
|
-
provider(preferred) {
|
|
11648
|
-
const names = this.subagents.list()
|
|
12009
|
+
provider(preferred, explicitNames = []) {
|
|
12010
|
+
const names = this.subagents.list?.() ?? [.../* @__PURE__ */ new Set([
|
|
12011
|
+
...this.providerNames,
|
|
12012
|
+
...configuredProviderNames(this.resultRuntime),
|
|
12013
|
+
...explicitNames
|
|
12014
|
+
])].filter((name) => this.subagents.getProvider(name) !== void 0);
|
|
11649
12015
|
const compatible = (name) => {
|
|
11650
12016
|
const provider = this.subagents.getProvider(name);
|
|
11651
12017
|
return provider?.capabilities.depthLimit === true && childComposition(this.subagents, provider) !== void 0;
|
|
@@ -11976,6 +12342,192 @@ var TurnActivityProjection = class {
|
|
|
11976
12342
|
}
|
|
11977
12343
|
};
|
|
11978
12344
|
//#endregion
|
|
12345
|
+
//#region src/host/memory-snapshot-state.ts
|
|
12346
|
+
const digest = (value) => createHash("sha256").update(canonicalMemoryJson(value)).digest("hex");
|
|
12347
|
+
const hash$1 = (value) => typeof value === "string" && /^[a-f0-9]{64}$/u.test(value);
|
|
12348
|
+
const record$2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12349
|
+
function readMemorySnapshotState(value) {
|
|
12350
|
+
if (!record$2(value) || value.version !== 1 || !hash$1(value.boundary) || !Array.isArray(value.entries) || !value.entries.every(hash$1)) return void 0;
|
|
12351
|
+
return {
|
|
12352
|
+
version: 1,
|
|
12353
|
+
boundary: value.boundary,
|
|
12354
|
+
entries: [...value.entries]
|
|
12355
|
+
};
|
|
12356
|
+
}
|
|
12357
|
+
/**
|
|
12358
|
+
* Only Runtime's Source-owned, complete-projection provenance permits additive
|
|
12359
|
+
* history. Unknown Sources and opaque grants are compared in full. Do not use
|
|
12360
|
+
* View ids or source revisions here: ordinary Runtime writes change both.
|
|
12361
|
+
*/
|
|
12362
|
+
function memorySnapshotState(view) {
|
|
12363
|
+
if (view === void 0) return void 0;
|
|
12364
|
+
const entries = [];
|
|
12365
|
+
const projection = view.projection.map((fragment) => {
|
|
12366
|
+
const provenance = fragment.provenance;
|
|
12367
|
+
const snapshot = record$2(provenance) ? provenance.snapshot : void 0;
|
|
12368
|
+
if (!record$2(provenance) || provenance.sourceTypeId !== "runtime" || !record$2(snapshot) || snapshot.version !== 1 || !hash$1(snapshot.scope) || !Array.isArray(snapshot.entries) || !snapshot.entries.every(hash$1)) return fragment;
|
|
12369
|
+
entries.push(...snapshot.entries.map((entry) => digest([
|
|
12370
|
+
fragment.sourceInstanceKey,
|
|
12371
|
+
fragment.id,
|
|
12372
|
+
entry
|
|
12373
|
+
])));
|
|
12374
|
+
const { text: _text, revision: _revision, ...identity } = fragment;
|
|
12375
|
+
return {
|
|
12376
|
+
...identity,
|
|
12377
|
+
provenance: {
|
|
12378
|
+
...provenance,
|
|
12379
|
+
snapshot: {
|
|
12380
|
+
version: 1,
|
|
12381
|
+
scope: snapshot.scope
|
|
12382
|
+
}
|
|
12383
|
+
}
|
|
12384
|
+
};
|
|
12385
|
+
});
|
|
12386
|
+
return {
|
|
12387
|
+
version: 1,
|
|
12388
|
+
boundary: digest({
|
|
12389
|
+
generation: view.runtimeGeneration,
|
|
12390
|
+
scope: view.scope,
|
|
12391
|
+
strategy: [
|
|
12392
|
+
view.strategyInstanceKey,
|
|
12393
|
+
view.strategyTypeId,
|
|
12394
|
+
view.strategyExtensions ?? []
|
|
12395
|
+
],
|
|
12396
|
+
projection,
|
|
12397
|
+
routes: view.routes,
|
|
12398
|
+
actions: view.actionOffers,
|
|
12399
|
+
grants: view.readGrants,
|
|
12400
|
+
guidance: view.guidance ?? null,
|
|
12401
|
+
diagnostics: view.diagnostics ?? []
|
|
12402
|
+
}),
|
|
12403
|
+
entries: entries.sort()
|
|
12404
|
+
};
|
|
12405
|
+
}
|
|
12406
|
+
/** A missing old occurrence is a deletion/correction, even with duplicate facts. */
|
|
12407
|
+
function retainsSnapshotEntries(previous, current) {
|
|
12408
|
+
const counts = /* @__PURE__ */ new Map();
|
|
12409
|
+
for (const entry of current) counts.set(entry, (counts.get(entry) ?? 0) + 1);
|
|
12410
|
+
for (const entry of previous) {
|
|
12411
|
+
const count = counts.get(entry) ?? 0;
|
|
12412
|
+
if (count === 0) return false;
|
|
12413
|
+
counts.set(entry, count - 1);
|
|
12414
|
+
}
|
|
12415
|
+
return true;
|
|
12416
|
+
}
|
|
12417
|
+
//#endregion
|
|
12418
|
+
//#region src/host/memory-snapshot.ts
|
|
12419
|
+
const MAX_SNAPSHOTS = 4;
|
|
12420
|
+
const MAX_SNAPSHOT_BYTES = 65536;
|
|
12421
|
+
const LEGACY_RUNTIME_SNAPSHOT = /^MNEMON RUNTIME MEMORY SNAPSHOT\n[^\n]+(?:\n[^\n]+)*\n\n(?:USER\.md \(user profile\)|Contents of USER\.md \(user profile; entries: \d+; UTF-8 bytes: \d+\/\d+\))\n<runtime-memory-file name="USER.md">\n(?:(?!\n\nMNEMON (?:RUNTIME MEMORY SNAPSHOT|VIEW ))[\s\S])*?\n<\/runtime-memory-file>\n\n(?:MEMORY\.md \(working reference\)|Contents of MEMORY\.md \(working reference; entries: \d+; UTF-8 bytes: \d+\/\d+\))\n<runtime-memory-file name="MEMORY.md">\n(?:(?!\n\nMNEMON (?:RUNTIME MEMORY SNAPSHOT|VIEW ))[\s\S])*?\n<\/runtime-memory-file>(?:$|\n\n)/u;
|
|
12422
|
+
const TRUNCATED_RUNTIME_START = /^MNEMON RUNTIME MEMORY SNAPSHOT\n(?:Revision: [a-f0-9]{64}(?:\nGit branch: [^\r\n]+)?(?:\nMetadata lines are annotations; created\/updated are ages at projection in whole days \(future\/unknown for future\/invalid timestamps\)\. Current instructions win\. For old_text\/oldText, use entry content only\.)?\n\nContents of USER\.md \(user profile; entries: \d+; UTF-8 bytes: \d+\/\d+\)|Entry annotations default to importance=normal and ages=0d \(whole days at projection; future\/unknown for future\/invalid timestamps\)\. Current instructions win\. For old_text\/oldText, use entry content only\.(?:\nGit branch: [^\r\n]+)?\n\nUSER\.md \(user profile\))\n<runtime-memory-file name="USER.md">\n/u;
|
|
12423
|
+
const RUNTIME_WAKE_SEPARATOR = /\n\n(?:MNEMON RUNTIME MEMORY SNAPSHOT\n|MNEMON VIEW TOOLS \(available in this View\): |MNEMON VIEW ROUTES \(quoted routing data; use mnemon_view_route or mnemon_view_action by exact id\): |MNEMON VIEW AVAILABILITY \(quoted diagnostics; unavailable Sources are not evidence\): )/u;
|
|
12424
|
+
function isTruncatedRuntimeSnapshot(text) {
|
|
12425
|
+
const start = TRUNCATED_RUNTIME_START.exec(text);
|
|
12426
|
+
if (start === null) return false;
|
|
12427
|
+
const body = text.slice(start[0].length);
|
|
12428
|
+
const end = body.search(RUNTIME_WAKE_SEPARATOR);
|
|
12429
|
+
const first = end < 0 ? body : body.slice(0, end);
|
|
12430
|
+
if (!first.endsWith("…")) return false;
|
|
12431
|
+
const prefix = first.slice(0, -1);
|
|
12432
|
+
const userEnd = prefix.indexOf("\n</runtime-memory-file>");
|
|
12433
|
+
if (userEnd < 0) return true;
|
|
12434
|
+
const heading = start[0].includes("\nContents of USER.md ") ? [
|
|
12435
|
+
"\n\nContents of MEMORY.md (working reference; entries: ",
|
|
12436
|
+
/^\d+/u,
|
|
12437
|
+
"; UTF-8 bytes: ",
|
|
12438
|
+
/^\d+/u,
|
|
12439
|
+
"/",
|
|
12440
|
+
/^\d+/u,
|
|
12441
|
+
")"
|
|
12442
|
+
] : ["\n\nMEMORY.md (working reference)"];
|
|
12443
|
+
heading.push("\n<runtime-memory-file name=\"MEMORY.md\">\n");
|
|
12444
|
+
let rest = prefix.slice(userEnd + 23);
|
|
12445
|
+
for (const part of heading) {
|
|
12446
|
+
if (rest === "") return true;
|
|
12447
|
+
if (typeof part === "string") {
|
|
12448
|
+
if (part.startsWith(rest)) return true;
|
|
12449
|
+
if (!rest.startsWith(part)) return false;
|
|
12450
|
+
rest = rest.slice(part.length);
|
|
12451
|
+
} else {
|
|
12452
|
+
const match = part.exec(rest);
|
|
12453
|
+
if (match === null) return false;
|
|
12454
|
+
rest = rest.slice(match[0].length);
|
|
12455
|
+
}
|
|
12456
|
+
}
|
|
12457
|
+
return true;
|
|
12458
|
+
}
|
|
12459
|
+
/** Read only this producer's snapshot formats; ordinary recalls retain history. */
|
|
12460
|
+
function snapshotText(event) {
|
|
12461
|
+
if (event?.type !== "user/message" || event.data.role !== "user") return void 0;
|
|
12462
|
+
const source = event.data.source;
|
|
12463
|
+
if (!isMnemonMessageSource(source)) return void 0;
|
|
12464
|
+
const content = event.data.content;
|
|
12465
|
+
if (!Array.isArray(content) || content.length !== 1) return void 0;
|
|
12466
|
+
const block = content[0];
|
|
12467
|
+
if (block?.type !== "text" || typeof block.text !== "string") return void 0;
|
|
12468
|
+
if (source?.form === "memory-snapshot") return block.text;
|
|
12469
|
+
if (source?.form !== "recall" || !(LEGACY_RUNTIME_SNAPSHOT.test(block.text) || isTruncatedRuntimeSnapshot(block.text))) return void 0;
|
|
12470
|
+
return block.text;
|
|
12471
|
+
}
|
|
12472
|
+
/**
|
|
12473
|
+
* Prepare a snapshot and its retirements without writing before step/start.
|
|
12474
|
+
* Commit after step/start publication, before the Host builds its request.
|
|
12475
|
+
* Empty developer nodes are a public Session representation that derives no model
|
|
12476
|
+
* message. They contain no instructions, Source text, or tool changes. Each
|
|
12477
|
+
* replacement shadows one owned node and cites it; the append-only audit keeps
|
|
12478
|
+
* the original full message, without touching interleaved conversation.
|
|
12479
|
+
*/
|
|
12480
|
+
function prepareMemorySnapshot(session, text, state) {
|
|
12481
|
+
const current = text?.trim() === "" ? void 0 : text;
|
|
12482
|
+
const snapshots = session.surface.nodes.flatMap((seq) => {
|
|
12483
|
+
const event = session.eventAt(seq);
|
|
12484
|
+
const text = snapshotText(event);
|
|
12485
|
+
const source = event?.data.source;
|
|
12486
|
+
return text === void 0 ? [] : [{
|
|
12487
|
+
seq,
|
|
12488
|
+
text,
|
|
12489
|
+
state: source?.form === "memory-snapshot" ? readMemorySnapshotState(source.snapshot) : void 0
|
|
12490
|
+
}];
|
|
12491
|
+
});
|
|
12492
|
+
const latest = snapshots.at(-1);
|
|
12493
|
+
const compatible = state !== void 0 && snapshots.every((snapshot) => snapshot.state?.boundary === state.boundary && retainsSnapshotEntries(snapshot.state.entries, state.entries));
|
|
12494
|
+
const unchanged = current !== void 0 && latest?.text === current;
|
|
12495
|
+
const count = snapshots.length + (unchanged ? 0 : 1);
|
|
12496
|
+
const bytes = snapshots.reduce((total, snapshot) => total + Buffer.byteLength(snapshot.text, "utf8"), 0) + (unchanged || current === void 0 ? 0 : Buffer.byteLength(current, "utf8"));
|
|
12497
|
+
const retained = current === void 0 ? [] : compatible && count <= MAX_SNAPSHOTS && (count <= 1 || bytes <= MAX_SNAPSHOT_BYTES) ? snapshots : state === void 0 && unchanged ? [latest] : [];
|
|
12498
|
+
const stale = snapshots.filter((snapshot) => !retained.includes(snapshot));
|
|
12499
|
+
return {
|
|
12500
|
+
message: current === void 0 || unchanged && retained.includes(latest) ? void 0 : createPluginMessage(current, "memory-snapshot", state),
|
|
12501
|
+
retire(turn, step) {
|
|
12502
|
+
const visible = new Set(session.surface.nodes);
|
|
12503
|
+
for (const { seq } of stale) {
|
|
12504
|
+
if (!visible.has(seq)) continue;
|
|
12505
|
+
if (session.append === void 0) throw new Error("DSH Session append is required to retire superseded memory snapshots");
|
|
12506
|
+
session.append("developer/message", {
|
|
12507
|
+
turn,
|
|
12508
|
+
step,
|
|
12509
|
+
message: {
|
|
12510
|
+
id: randomUUID(),
|
|
12511
|
+
role: "developer",
|
|
12512
|
+
content: [],
|
|
12513
|
+
source: {
|
|
12514
|
+
kind: MNEMON_PLUGIN_SOURCE,
|
|
12515
|
+
form: "memory-snapshot-retired"
|
|
12516
|
+
}
|
|
12517
|
+
}
|
|
12518
|
+
}, {
|
|
12519
|
+
surfaceOp: {
|
|
12520
|
+
op: "replace",
|
|
12521
|
+
startSeq: seq,
|
|
12522
|
+
endSeq: seq
|
|
12523
|
+
},
|
|
12524
|
+
sourceEventSeqs: [seq]
|
|
12525
|
+
});
|
|
12526
|
+
}
|
|
12527
|
+
}
|
|
12528
|
+
};
|
|
12529
|
+
}
|
|
12530
|
+
//#endregion
|
|
11979
12531
|
//#region src/host/idle-review-watermark.ts
|
|
11980
12532
|
const MAX_STATE_BYTES = 16384;
|
|
11981
12533
|
const MAX_TIMESTAMP = 864e13;
|
|
@@ -12140,11 +12692,6 @@ function llmService(value) {
|
|
|
12140
12692
|
if (typeof value !== "object" || value === null || !("listProviders" in value) || typeof value.listProviders !== "function" || !("listModels" in value) || typeof value.listModels !== "function") return void 0;
|
|
12141
12693
|
return value;
|
|
12142
12694
|
}
|
|
12143
|
-
function isMnemonMessageSource(source) {
|
|
12144
|
-
if (typeof source !== "object" || source === null) return false;
|
|
12145
|
-
const { kind, plugin } = source;
|
|
12146
|
-
return kind === "dsh-mnemon" || kind === `plugin:dsh-mnemon` || kind === "plugin" && plugin === "dsh-mnemon";
|
|
12147
|
-
}
|
|
12148
12695
|
/**
|
|
12149
12696
|
* Whether an event is a durable user message this plugin produced.
|
|
12150
12697
|
*/
|
|
@@ -12224,16 +12771,9 @@ var MnemonAgentLifecycle = class {
|
|
|
12224
12771
|
primePending = true;
|
|
12225
12772
|
startSource;
|
|
12226
12773
|
guidedTurns = /* @__PURE__ */ new Set();
|
|
12774
|
+
pendingSnapshot;
|
|
12227
12775
|
memoryActivity = new TurnActivityProjection();
|
|
12228
12776
|
turnActivity = /* @__PURE__ */ new Map();
|
|
12229
|
-
/**
|
|
12230
|
-
* Text of the runtime memory snapshot most recently injected as this
|
|
12231
|
-
* plugin's own message. `.context()` used to get supersede-on-change for free
|
|
12232
|
-
* from the host's runtime-context projection; carrying the snapshot as an own
|
|
12233
|
-
* message means re-emitting on change is this plugin's responsibility, and
|
|
12234
|
-
* the rendered text (which carries the revision digest) is the key.
|
|
12235
|
-
*/
|
|
12236
|
-
injectedMemoryText;
|
|
12237
12777
|
idleReviewTimer;
|
|
12238
12778
|
reviewController;
|
|
12239
12779
|
reviewRunning = false;
|
|
@@ -12279,6 +12819,10 @@ var MnemonAgentLifecycle = class {
|
|
|
12279
12819
|
];
|
|
12280
12820
|
return () => {
|
|
12281
12821
|
this.disposed = true;
|
|
12822
|
+
const pending = this.pendingSnapshot;
|
|
12823
|
+
this.pendingSnapshot = void 0;
|
|
12824
|
+
const started = pending === void 0 ? void 0 : this.agent.session.snapshotEvents().findLast((event) => event.type === "step/start");
|
|
12825
|
+
if (pending !== void 0 && started !== void 0 && eventTurn(started) === pending.turn && started.data.step === pending.step) this.queueSnapshotRetirement(pending);
|
|
12282
12826
|
this.releaseView();
|
|
12283
12827
|
this.cancelIdleReview(true);
|
|
12284
12828
|
for (const dispose of disposers.reverse()) dispose();
|
|
@@ -12348,24 +12892,8 @@ var MnemonAgentLifecycle = class {
|
|
|
12348
12892
|
for (const seq of this.agent.session.surface.nodes) if (isOwnUserMessageEvent(this.agent.session.eventAt(seq))) return true;
|
|
12349
12893
|
return false;
|
|
12350
12894
|
}
|
|
12351
|
-
/**
|
|
12352
|
-
* The runtime memory snapshot, as this plugin's own message, when it changed.
|
|
12353
|
-
*
|
|
12354
|
-
* `.context()` used to carry this inside the host's shared runtime-context
|
|
12355
|
-
* projection. Carrying it here instead attributes it to dsh-mnemon and keeps
|
|
12356
|
-
* a memory write from re-emitting other contributors' sections; supersede
|
|
12357
|
-
* stays intact because the block is still a complete state replacing its
|
|
12358
|
-
* predecessor, keyed on the rendered text's revision digest.
|
|
12359
|
-
*/
|
|
12360
|
-
memorySnapshotMessage() {
|
|
12361
|
-
const wake = this.memoryWake();
|
|
12362
|
-
if (wake === void 0) return void 0;
|
|
12363
|
-
const text = wake.text;
|
|
12364
|
-
if (text.trim() === "" || text === this.injectedMemoryText) return void 0;
|
|
12365
|
-
this.injectedMemoryText = text;
|
|
12366
|
-
return createPluginMessage(text, "recall");
|
|
12367
|
-
}
|
|
12368
12895
|
async preStep(payload, next) {
|
|
12896
|
+
this.pendingSnapshot = void 0;
|
|
12369
12897
|
if (payload.step === 1) this.cancelIdleReview(true);
|
|
12370
12898
|
const decision = await next();
|
|
12371
12899
|
if (decision.kind === "reject" || payload.signal.aborted) {
|
|
@@ -12373,12 +12901,19 @@ var MnemonAgentLifecycle = class {
|
|
|
12373
12901
|
return decision;
|
|
12374
12902
|
}
|
|
12375
12903
|
if (!this.disposed && openAgentTurn(this.agent) === payload.turn) await this.memoryTurn?.begin(payload.turn, payload.signal);
|
|
12904
|
+
const update = prepareMemorySnapshot(this.agent.session, this.config.lifecycleEnabled ? this.memoryWake()?.text : void 0, this.config.lifecycleEnabled ? memorySnapshotState(this.memoryTurn?.current?.context.view) : void 0);
|
|
12905
|
+
this.pendingSnapshot = {
|
|
12906
|
+
turn: payload.turn,
|
|
12907
|
+
step: payload.step,
|
|
12908
|
+
signal: payload.signal,
|
|
12909
|
+
update
|
|
12910
|
+
};
|
|
12376
12911
|
if (!this.config.lifecycleEnabled) return decision;
|
|
12377
12912
|
if (this.config.writeEnabled && this.config.writebackMode === "guided") this.recordTurnMessages(payload.turn, decision.messages);
|
|
12378
12913
|
if (payload.step !== 1) return decision;
|
|
12379
12914
|
if (decision.messages.some((message) => isMnemonMessageSource(message.source))) return decision;
|
|
12380
12915
|
if (decision.messages.length === 0) return decision;
|
|
12381
|
-
const snapshot =
|
|
12916
|
+
const snapshot = update.message;
|
|
12382
12917
|
const withSnapshot = (messages) => snapshot === void 0 ? messages : [...messages, snapshot];
|
|
12383
12918
|
if (this.primePending) {
|
|
12384
12919
|
this.primePending = false;
|
|
@@ -12447,12 +12982,40 @@ var MnemonAgentLifecycle = class {
|
|
|
12447
12982
|
if (admitted) this.scheduleIdleReview(payload.turn);
|
|
12448
12983
|
}
|
|
12449
12984
|
sessionEvent(session, event) {
|
|
12450
|
-
if (session !== this.agent.session
|
|
12985
|
+
if (session !== this.agent.session) return;
|
|
12451
12986
|
const turn = eventTurn(event);
|
|
12987
|
+
if (event.type === "step/start") {
|
|
12988
|
+
const pending = this.pendingSnapshot;
|
|
12989
|
+
if (pending === void 0 || pending.turn !== turn || pending.step !== event.data.step) return;
|
|
12990
|
+
this.pendingSnapshot = void 0;
|
|
12991
|
+
this.queueSnapshotRetirement(pending);
|
|
12992
|
+
return;
|
|
12993
|
+
}
|
|
12994
|
+
if (event.type !== "turn/end") return;
|
|
12995
|
+
if (this.pendingSnapshot?.turn === turn) this.pendingSnapshot = void 0;
|
|
12452
12996
|
const current = this.memoryTurn?.current;
|
|
12453
12997
|
if ((turn === void 0 || current?.turn !== turn ? void 0 : current) === void 0) return;
|
|
12454
12998
|
this.releaseView(turn);
|
|
12455
12999
|
}
|
|
13000
|
+
queueSnapshotRetirement(pending) {
|
|
13001
|
+
queueMicrotask(() => {
|
|
13002
|
+
if (pending.signal.aborted || openAgentTurn(this.agent) !== pending.turn) return;
|
|
13003
|
+
try {
|
|
13004
|
+
pending.update.retire(pending.turn, pending.step);
|
|
13005
|
+
} catch {
|
|
13006
|
+
const reason = "Memory snapshot checkpoint failed; model request cancelled.";
|
|
13007
|
+
this.counters.failures += 1;
|
|
13008
|
+
this.lastPhase = "error";
|
|
13009
|
+
this.lastAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13010
|
+
this.lastError = reason;
|
|
13011
|
+
this.agent.cancel({
|
|
13012
|
+
kind: "hook",
|
|
13013
|
+
reason
|
|
13014
|
+
}, { keepInbox: true });
|
|
13015
|
+
console.warn("[dsh-mnemon] Memory snapshot checkpoint failed; model request cancelled.");
|
|
13016
|
+
}
|
|
13017
|
+
});
|
|
13018
|
+
}
|
|
12456
13019
|
scheduleIdleReview(turn) {
|
|
12457
13020
|
this.cancelIdleReview(true);
|
|
12458
13021
|
if (!this.restoreWatermark() || !this.idleReviewAllowed()) return;
|
|
@@ -12765,7 +13328,7 @@ var MnemonLifecycle = class {
|
|
|
12765
13328
|
}
|
|
12766
13329
|
/** Model work for skill evolution under a fresh task Agent, outside any conversation. */
|
|
12767
13330
|
runSkillTask(workspaceRoot, signal, operation, route) {
|
|
12768
|
-
return this.runTaskAgent("", workspaceRoot, signal, operation, route);
|
|
13331
|
+
return this.runTaskAgent("", workspaceRoot, signal, operation, route, "Mnemon · Skills");
|
|
12769
13332
|
}
|
|
12770
13333
|
/**
|
|
12771
13334
|
* What one conversation did with skills, for the bars under its turns. A
|
|
@@ -12808,7 +13371,7 @@ var MnemonLifecycle = class {
|
|
|
12808
13371
|
const stopCreated = this.ctx.on("agent/created", (({ agent, source }) => {
|
|
12809
13372
|
this.install(agent, source);
|
|
12810
13373
|
}));
|
|
12811
|
-
for (const agent of this.ctx.agents.
|
|
13374
|
+
for (const agent of rootAgents(this.ctx.agents, this.ctx.subagents)) this.install(agent, "adopted");
|
|
12812
13375
|
return () => {
|
|
12813
13376
|
stopChanged?.();
|
|
12814
13377
|
stopCreated();
|
|
@@ -12988,7 +13551,7 @@ var MnemonLifecycle = class {
|
|
|
12988
13551
|
/** Synthesize a Web Agent Query without borrowing a conversation Agent or its history. */
|
|
12989
13552
|
answerTask(sessionId, query, evidence, workspaceRoot, signal = new AbortController().signal) {
|
|
12990
13553
|
const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
|
|
12991
|
-
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.answer(agent, query, evidence, signal));
|
|
13554
|
+
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.answer(agent, query, evidence, signal), void 0, "Mnemon · Ask Agent");
|
|
12992
13555
|
}
|
|
12993
13556
|
/** A write the user sent from the page: a space they picked that is switched off comes back on (#387). */
|
|
12994
13557
|
remember(sessionId, request, signal = new AbortController().signal) {
|
|
@@ -13001,7 +13564,7 @@ var MnemonLifecycle = class {
|
|
|
13001
13564
|
return this.coordinator.manageSource(graph, request);
|
|
13002
13565
|
}
|
|
13003
13566
|
runRuntimeMaintenanceTask(scope, signal, operation) {
|
|
13004
|
-
return this.runTaskAgent(scope.sessionId ?? "", scope.workspaceId, signal, operation);
|
|
13567
|
+
return this.runTaskAgent(scope.sessionId ?? "", scope.workspaceId, signal, operation, void 0, "Mnemon · Memory maintenance");
|
|
13005
13568
|
}
|
|
13006
13569
|
/**
|
|
13007
13570
|
* Read the merged memory and let a model propose what a human should review.
|
|
@@ -13030,12 +13593,12 @@ var MnemonLifecycle = class {
|
|
|
13030
13593
|
mutateDocumentTask(sessionId, request, workspaceRoot, signal = new AbortController().signal) {
|
|
13031
13594
|
const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
|
|
13032
13595
|
if (root === void 0 || root.trim() === "") throw new Error("a selected DSH workspace is required to write a Mnemon Document");
|
|
13033
|
-
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.document(agent, request, signal));
|
|
13596
|
+
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.document(agent, request, signal), void 0, "Mnemon · Document");
|
|
13034
13597
|
}
|
|
13035
13598
|
archiveDocument(sessionId, id, workspaceRoot, signal = new AbortController().signal) {
|
|
13036
13599
|
const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
|
|
13037
13600
|
if (root === void 0 || root.trim() === "") throw new Error("a selected DSH workspace is required to archive a Mnemon Document");
|
|
13038
|
-
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.archiveDocument(agent, id, signal));
|
|
13601
|
+
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.archiveDocument(agent, id, signal), void 0, "Mnemon · Archive Document");
|
|
13039
13602
|
}
|
|
13040
13603
|
mutate(sessionId, operation, request, signal = new AbortController().signal) {
|
|
13041
13604
|
return this.coordinator.write(this.liveAgent(sessionId), operation, request, signal);
|
|
@@ -13046,11 +13609,11 @@ var MnemonLifecycle = class {
|
|
|
13046
13609
|
/** Choose a new Memory Space's Provider under a fresh task Agent, as Ask Agent does without a loaded conversation Agent. */
|
|
13047
13610
|
placeProviderTask(sessionId, body, prepared, workspaceRoot, signal = new AbortController().signal) {
|
|
13048
13611
|
const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
|
|
13049
|
-
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.placeProvider(agent, body, prepared, signal));
|
|
13612
|
+
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.placeProvider(agent, body, prepared, signal), void 0, "Mnemon · Choose memory provider");
|
|
13050
13613
|
}
|
|
13051
13614
|
maintainMetadata(sessionId, memoryBodyIds, workspaceRoot, signal = new AbortController().signal) {
|
|
13052
13615
|
const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
|
|
13053
|
-
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.maintainMetadata(agent, memoryBodyIds, signal));
|
|
13616
|
+
return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.maintainMetadata(agent, memoryBodyIds, signal), void 0, "Mnemon · Memory metadata");
|
|
13054
13617
|
}
|
|
13055
13618
|
async supervise(sessionId, content, idempotencyKey, signal = new AbortController().signal) {
|
|
13056
13619
|
const normalizedSessionId = sessionId.trim();
|
|
@@ -13062,7 +13625,7 @@ var MnemonLifecycle = class {
|
|
|
13062
13625
|
const normalizedSessionId = sessionId.trim();
|
|
13063
13626
|
const root = workspaceRoot?.trim() || this.workspaceRoot(normalizedSessionId);
|
|
13064
13627
|
const scopeKey = root === void 0 ? `task:${normalizedSessionId || "global"}` : `task:${resolve(root)}`;
|
|
13065
|
-
return this.superviseResolved(scopeKey, normalizedSessionId, content, idempotencyKey, signal, (operation) => this.runTaskAgent(normalizedSessionId, root, signal, operation));
|
|
13628
|
+
return this.superviseResolved(scopeKey, normalizedSessionId, content, idempotencyKey, signal, (operation) => this.runTaskAgent(normalizedSessionId, root, signal, operation, void 0, "Mnemon · Save to memory"));
|
|
13066
13629
|
}
|
|
13067
13630
|
async superviseResolved(replayScope, responseSessionId, content, idempotencyKey, signal, withAgent) {
|
|
13068
13631
|
if (!this.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only (writeEnabled: false)");
|
|
@@ -13116,8 +13679,9 @@ var MnemonLifecycle = class {
|
|
|
13116
13679
|
* is the explicit Web workbench scope, so LiveMnemonRuntime resolves the same
|
|
13117
13680
|
* workspace graph without borrowing conversation history or ownership.
|
|
13118
13681
|
*/
|
|
13119
|
-
async runTaskAgent(fallbackSessionId, workspaceRoot, signal, operation, route) {
|
|
13120
|
-
const
|
|
13682
|
+
async runTaskAgent(fallbackSessionId, workspaceRoot, signal, operation, route, title = "Mnemon · Background task") {
|
|
13683
|
+
const modern = activationOwned(this.ctx.subagents);
|
|
13684
|
+
const sessionId = `mnemon-task-v1-${randomUUID()}`;
|
|
13121
13685
|
this.taskAgentIds.add(sessionId);
|
|
13122
13686
|
let handle;
|
|
13123
13687
|
let failure;
|
|
@@ -13128,6 +13692,7 @@ var MnemonLifecycle = class {
|
|
|
13128
13692
|
...creation,
|
|
13129
13693
|
signal
|
|
13130
13694
|
});
|
|
13695
|
+
if (modern) titleTask(handle.agent, title);
|
|
13131
13696
|
if (route !== void 0) try {
|
|
13132
13697
|
handle.agent[TASK_ROUTE] = route;
|
|
13133
13698
|
} catch {}
|
|
@@ -13153,7 +13718,9 @@ var MnemonLifecycle = class {
|
|
|
13153
13718
|
* a turn only while DSH's projection cache holds a record of it, so a finished task
|
|
13154
13719
|
* could stay listed as an untitled Ungrouped conversation (#435). No `parentSession`
|
|
13155
13720
|
* or `delegationDepth`: the task belongs to no conversation, and its children stay
|
|
13156
|
-
* one level deep.
|
|
13721
|
+
* one level deep. Activation-owned master requires an ordinary root instead;
|
|
13722
|
+
* its persistent task ID and title identify the visible record without enrolling
|
|
13723
|
+
* it in conversation automation. Official rc.1 retains the hidden metadata.
|
|
13157
13724
|
*/
|
|
13158
13725
|
async taskAgentCreation(fallbackSessionId, workspaceRoot, signal, route) {
|
|
13159
13726
|
const baseOptions = this.taskAgentModelOptions(fallbackSessionId, workspaceRoot);
|
|
@@ -13171,7 +13738,7 @@ var MnemonLifecycle = class {
|
|
|
13171
13738
|
const workingDirectory = workFrom === void 0 ? void 0 : this.ctx.get("workingDirectory");
|
|
13172
13739
|
const meta = {
|
|
13173
13740
|
cwd,
|
|
13174
|
-
origin: "subagent",
|
|
13741
|
+
...activationOwned(this.ctx.subagents) ? {} : { origin: "subagent" },
|
|
13175
13742
|
...presetId === void 0 ? {} : { agentPreset: presetId }
|
|
13176
13743
|
};
|
|
13177
13744
|
if (presets === void 0 && typeof workingDirectory?.set !== "function") return {
|
|
@@ -13309,11 +13876,15 @@ var MnemonLifecycle = class {
|
|
|
13309
13876
|
}
|
|
13310
13877
|
install(agent, source) {
|
|
13311
13878
|
if (this.taskAgentIds.has(agent.id) || this.owners.has(agent) || this.children.has(agent)) return;
|
|
13879
|
+
if ((isTaskRecord(agent.id) || this.coordinator.isFinishedTask(agent.id, agent)) && !isCreatingChild(this.ctx.agents, agent)) {
|
|
13880
|
+
this.coordinator.continueTask(agent);
|
|
13881
|
+
return;
|
|
13882
|
+
}
|
|
13312
13883
|
if (agent.session.header?.origin === "subagent") {
|
|
13313
13884
|
this.installChild(agent);
|
|
13314
13885
|
return;
|
|
13315
13886
|
}
|
|
13316
|
-
if (!this.ctx.agents.
|
|
13887
|
+
if (!isRootAgent(this.ctx.agents, this.ctx.subagents, agent)) return;
|
|
13317
13888
|
const memoryTurn = this.runtimeSource === void 0 ? void 0 : new AgentMemoryTurn(agent, this.runtimeSource);
|
|
13318
13889
|
const lifecycle = new MnemonAgentLifecycle(agent, this.coordinator, this.config, this.counters, source, memoryTurn, () => this.runtimeSource?.forAgent(agent).directory);
|
|
13319
13890
|
let dispose;
|
|
@@ -13343,6 +13914,7 @@ var MnemonLifecycle = class {
|
|
|
13343
13914
|
const parentId = agent.session.header?.parentSession?.trim();
|
|
13344
13915
|
const parent = parentId === void 0 || parentId === "" ? void 0 : this.ctx.agents.get(parentId);
|
|
13345
13916
|
if (runtime === void 0 || parent === void 0 || parent === agent) return;
|
|
13917
|
+
this.coordinator.inheritTaskContinuation(parent, agent);
|
|
13346
13918
|
const parentMemory = this.memoryTurns.get(parent);
|
|
13347
13919
|
let delegation;
|
|
13348
13920
|
if (parentMemory !== void 0) delegation = parentMemory.delegate();
|
|
@@ -14569,12 +15141,15 @@ var SkillEvolution = class {
|
|
|
14569
15141
|
const source = retryingSource(port);
|
|
14570
15142
|
const pack = await source.read("evidence-pack", {
|
|
14571
15143
|
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
|
|
15144
|
+
...input.draftId === void 0 ? {} : { draftId: input.draftId },
|
|
14572
15145
|
...input.skill === void 0 ? {} : { skill: input.skill }
|
|
14573
15146
|
});
|
|
14574
|
-
const
|
|
15147
|
+
const previousId = input.draftId ?? pack.suggestion?.draftId;
|
|
15148
|
+
const previous = previousId === void 0 ? (await source.read("drafts", {
|
|
14575
15149
|
skill: pack.skillName,
|
|
15150
|
+
...pack.skill === void 0 ? {} : { path: pack.skill.path },
|
|
14576
15151
|
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId }
|
|
14577
|
-
})).find((draft) => draft.suggestionId === input.suggestionId) : (await source.read("draft", { id:
|
|
15152
|
+
})).find((draft) => draft.suggestionId === input.suggestionId) : (await source.read("draft", { id: previousId })).draft;
|
|
14578
15153
|
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
15154
|
const guidance = [...previous?.guidance ?? [], ...input.guidance?.trim() ? [input.guidance.trim()] : []].slice(-8);
|
|
14580
15155
|
if (input.suggestionId !== void 0) await source.mutate("suggestion-status", {
|
|
@@ -14606,12 +15181,33 @@ var SkillEvolution = class {
|
|
|
14606
15181
|
const settings = this.host.settings();
|
|
14607
15182
|
const { pack, targetRoot, guidance } = prepared;
|
|
14608
15183
|
let prompt = skillDraftPrompt(pack, guidance, targetRoot);
|
|
15184
|
+
let maxTokens = settings.draftMaxTokens;
|
|
15185
|
+
const attemptedBudgets = [];
|
|
14609
15186
|
let draft;
|
|
14610
15187
|
for (let attempt = 0; draft === void 0; attempt++) {
|
|
14611
|
-
|
|
15188
|
+
signal.throwIfAborted();
|
|
15189
|
+
attemptedBudgets.push(maxTokens);
|
|
15190
|
+
let answer;
|
|
15191
|
+
try {
|
|
15192
|
+
answer = await this.host.runTask(workspace, signal, (task) => this.host.skillTask(task, "Mnemon skill draft", prompt, SKILL_DRAFT_SCHEMA, SKILL_DRAFT_PERSONA, signal, input.route, maxTokens), input.route);
|
|
15193
|
+
signal.throwIfAborted();
|
|
15194
|
+
} catch (error) {
|
|
15195
|
+
signal.throwIfAborted();
|
|
15196
|
+
if (error instanceof SubagentStoppedError && error.maxTokens !== void 0) attemptedBudgets[attemptedBudgets.length - 1] = error.maxTokens;
|
|
15197
|
+
if (error instanceof SubagentStoppedError && error.stopReason === "max-tokens" && attempt === 0 && settings.draftRetryMaxTokens > maxTokens) {
|
|
15198
|
+
maxTokens = settings.draftRetryMaxTokens;
|
|
15199
|
+
prompt += "\n\nThe previous attempt exhausted its output budget and was discarded. Return a complete SKILL.md from the beginning; keep notes and changes concise.";
|
|
15200
|
+
continue;
|
|
15201
|
+
}
|
|
15202
|
+
const bytes = Buffer.byteLength(pack.skill?.content ?? "", "utf8");
|
|
15203
|
+
throw new Error(`Skill draft failed. Attempted budgets: ${attemptedBudgets.join(", ")} tokens; source SKILL.md: ${bytes} bytes (UTF-8). Adjust skills.draftMaxTokens / skills.draftRetryMaxTokens in Plugins → dsh-mnemon → Skills → Draft generation. Stay within provider/model caps: lower rejected budgets, shorten the skill or use a model with a higher output cap. Reason: ${message(error).slice(0, 100)}`, { cause: error });
|
|
15204
|
+
}
|
|
15205
|
+
const parsed = parseSkillDraft(answer.structured);
|
|
14612
15206
|
try {
|
|
15207
|
+
signal.throwIfAborted();
|
|
14613
15208
|
draft = await source.mutate("draft-record", {
|
|
14614
15209
|
...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
|
|
15210
|
+
...input.draftId === void 0 ? {} : { draftId: input.draftId },
|
|
14615
15211
|
skillName: pack.skillName,
|
|
14616
15212
|
content: parsed.content,
|
|
14617
15213
|
notes: parsed.notes,
|
|
@@ -14620,6 +15216,7 @@ var SkillEvolution = class {
|
|
|
14620
15216
|
...pack.action === "create" ? { targetRoot } : {}
|
|
14621
15217
|
}, signal);
|
|
14622
15218
|
} catch (error) {
|
|
15219
|
+
signal.throwIfAborted();
|
|
14623
15220
|
if (attempt > 0) throw error;
|
|
14624
15221
|
prompt += `\n\nYour previous SKILL.md was rejected: ${message(error)}. Return a corrected complete file.`;
|
|
14625
15222
|
}
|
|
@@ -14640,7 +15237,10 @@ var SkillEvolution = class {
|
|
|
14640
15237
|
else {
|
|
14641
15238
|
const baseline = await this.backtest(source, workspace, {
|
|
14642
15239
|
skillName: pack.skillName,
|
|
14643
|
-
target: {
|
|
15240
|
+
target: {
|
|
15241
|
+
kind: pack.action === "update" ? "current" : "none",
|
|
15242
|
+
...pack.action === "update" ? { draftId: draft.id } : {}
|
|
15243
|
+
},
|
|
14644
15244
|
...input.route === void 0 ? {} : { route: input.route }
|
|
14645
15245
|
});
|
|
14646
15246
|
await source.mutate("run-link", {
|
|
@@ -15176,14 +15776,16 @@ async function skillAssistance(runtime, lifecycle, operation, input, signal) {
|
|
|
15176
15776
|
"user-agents"
|
|
15177
15777
|
].includes(root)) throw new Error("unknown skill root: " + root);
|
|
15178
15778
|
const suggestionId = optionalString(input.suggestionId, "suggestionId");
|
|
15779
|
+
const draftId = optionalString(input.draftId, "draftId");
|
|
15179
15780
|
const skill = optionalString(input.skill, "skill");
|
|
15180
|
-
if (suggestionId === void 0 && skill === void 0) throw new Error("a draft needs a suggestion or a skill");
|
|
15781
|
+
if (suggestionId === void 0 && draftId === void 0 && skill === void 0) throw new Error("a draft needs a suggestion, a draft, or a skill");
|
|
15181
15782
|
const guidance = optionalString(input.guidance, "guidance");
|
|
15182
15783
|
if (guidance !== void 0 && guidance.length > 4e3) throw new Error("guidance is too long (max 4000 characters)");
|
|
15183
15784
|
const draftRoute = modelRoute(input);
|
|
15184
15785
|
return skills.draft(source, workspaceRoot, {
|
|
15185
15786
|
...suggestionId === void 0 ? {} : { suggestionId },
|
|
15186
15787
|
...skill === void 0 ? {} : { skill },
|
|
15788
|
+
...draftId === void 0 ? {} : { draftId },
|
|
15187
15789
|
...guidance === void 0 ? {} : { guidance },
|
|
15188
15790
|
...root === void 0 ? {} : { targetRoot: root },
|
|
15189
15791
|
...draftRoute === void 0 ? {} : { route: draftRoute }
|
|
@@ -16415,7 +17017,7 @@ function registerDocumentTools(ctx, { requireSource, config, sourceAction }, coo
|
|
|
16415
17017
|
const agent = requireAgent(exec);
|
|
16416
17018
|
if (args.action === "archive") {
|
|
16417
17019
|
requireSource(exec, "documents", "archive");
|
|
16418
|
-
if (isSubagent(agent)) throw new Error("idle document workers cannot cold-archive directly");
|
|
17020
|
+
if (isSubagent(agent) && !coordinator.isUserContinuation(agent)) throw new Error("idle document workers cannot cold-archive directly");
|
|
16419
17021
|
if (args.id === void 0) throw new Error("document id is required for archive");
|
|
16420
17022
|
return Promise.resolve(coordinator.archiveDocument(agent, args.id, exec.signal)).then(documentReceipt);
|
|
16421
17023
|
}
|
|
@@ -18885,7 +19487,7 @@ function apply(rawContext, rawConfig = {}) {
|
|
|
18885
19487
|
settings: () => skillSettings.get(),
|
|
18886
19488
|
source: (agent) => skillsSourceFor(runtime, agent),
|
|
18887
19489
|
runTask: (workspaceRoot, signal, operation, route) => lifecycle.runSkillTask(workspaceRoot, signal, operation, route),
|
|
18888
|
-
skillTask: (parent, label, prompt, schema, persona, signal) => coordinator.skillTask(parent, label, prompt, schema, persona, signal, taskRoute(parent))
|
|
19490
|
+
skillTask: (parent, label, prompt, schema, persona, signal, _route, maxTokens) => coordinator.skillTask(parent, label, prompt, schema, persona, signal, taskRoute(parent), maxTokens)
|
|
18889
19491
|
});
|
|
18890
19492
|
lifecycle.useSkillEvolution(skills);
|
|
18891
19493
|
ctx.effect(() => {
|