dsh-mnemon 0.5.38 → 0.5.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/repair-legacy-session.mjs +86 -10
- package/lib/bundle.js +1 -1
- package/lib/client.js +49 -6
- package/lib/index.js +695 -573
- package/lib/{starter-resolution-XVFzvnV0.js → starter-resolution-C3yJawlA.js} +1 -1
- package/lib/starter.js +1 -1
- package/lib/types/src/client/locales.d.ts +5 -0
- package/lib/types/src/host/protocol.d.ts +3 -1
- package/package.json +35 -35
|
@@ -614,6 +614,89 @@ function encodeFrames(text) {
|
|
|
614
614
|
return Buffer.concat([text.slice(0, end), text.slice(end)].filter(Boolean).map(part => zlib.zstdCompressSync(part, options)))
|
|
615
615
|
}
|
|
616
616
|
|
|
617
|
+
async function writeRepairCopy(input, output, result) {
|
|
618
|
+
if (resolve(input) === resolve(output)) throw new Error('Output must be a new copy; the input cannot be overwritten.')
|
|
619
|
+
const temporary = await mkdtemp(join(dirname(resolve(output)), '.mnemon-repair-'))
|
|
620
|
+
try {
|
|
621
|
+
const path = join(temporary, 'session')
|
|
622
|
+
const file = await open(path, 'wx', 0o600)
|
|
623
|
+
try { await file.writeFile(result); await file.sync() } finally { await file.close() }
|
|
624
|
+
// Publish exclusively, including when output is a symlink or hard link.
|
|
625
|
+
await link(path, output)
|
|
626
|
+
} finally { await rm(temporary, { recursive: true, force: true }) }
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Preserve the complete audit payload and all offsets; never truncate history. */
|
|
630
|
+
async function repairIdleProbeSession(entries, original, compressed, input, output) {
|
|
631
|
+
const header = entries[0].row
|
|
632
|
+
assertUniqueJson(entries[0].line, header)
|
|
633
|
+
if (header.type !== 'session' || header.version !== 4 || header.isSeeded !== false || header.delegationDepth !== 0 ||
|
|
634
|
+
header.parentSession !== undefined || header.origin !== undefined) throw new Error('Idle probe recovery requires an unseeded v4 root Session.')
|
|
635
|
+
const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u
|
|
636
|
+
const recovered = new Set()
|
|
637
|
+
const pending = new Map()
|
|
638
|
+
const roots = new Set()
|
|
639
|
+
let openTurn
|
|
640
|
+
let completed = false
|
|
641
|
+
const refuse = () => { throw new Error('Unrecognized or incomplete out-of-turn PTC audit; no output written.') }
|
|
642
|
+
for (const entry of entries.slice(1)) {
|
|
643
|
+
const row = entry.row
|
|
644
|
+
if (row.type === 'turn/start' || row.type === 'turn/end') {
|
|
645
|
+
assertUniqueJson(entry.line, row)
|
|
646
|
+
if (pending.size || !count(row.data?.turn)) refuse()
|
|
647
|
+
if (row.type === 'turn/start') {
|
|
648
|
+
if (openTurn !== undefined) refuse()
|
|
649
|
+
openTurn = row.data.turn
|
|
650
|
+
} else {
|
|
651
|
+
if (openTurn !== row.data.turn) refuse()
|
|
652
|
+
openTurn = undefined
|
|
653
|
+
completed = true
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (!['tool/ptc-dispatch-start', 'tool/ptc-dispatch'].includes(row.type) || openTurn !== undefined) continue
|
|
657
|
+
assertUniqueJson(entry.line, row)
|
|
658
|
+
const data = row.data
|
|
659
|
+
const start = row.type === 'tool/ptc-dispatch-start'
|
|
660
|
+
if (!completed || !exactKeys(row, ['type', 'seq', 'time', 'data']) || !count(row.seq) || !integer(row.time) ||
|
|
661
|
+
!exactKeys(data, ['rootCallId', 'parentCallId', 'subCallId', 'name', 'arguments', ...(start ? [] : ['isError', 'content'])], start ? [] : ['error', 'meta']) ||
|
|
662
|
+
!uuid.test(data.rootCallId) || data.parentCallId !== data.rootCallId || data.subCallId !== `${data.rootCallId}:ptc:1` ||
|
|
663
|
+
data.name !== 'mnemon_subagent_result' || !exactKeys(data.arguments, ['requestId', 'result']) ||
|
|
664
|
+
!uuid.test(data.arguments.requestId) || !exactKeys(data.arguments.result, [])) refuse()
|
|
665
|
+
if (start) {
|
|
666
|
+
if (roots.has(data.rootCallId)) refuse()
|
|
667
|
+
roots.add(data.rootCallId)
|
|
668
|
+
pending.set(data.rootCallId, entry)
|
|
669
|
+
} else {
|
|
670
|
+
const previous = pending.get(data.rootCallId)
|
|
671
|
+
if (!previous || !isDeepStrictEqual(previous.row.data.arguments, data.arguments) || previous.row.seq >= row.seq ||
|
|
672
|
+
typeof data.isError !== 'boolean' || !Array.isArray(data.content) ||
|
|
673
|
+
!data.isError && !isDeepStrictEqual(data.content, [{ type: 'text', text: 'Mnemon subagent result recorded.' }])) refuse()
|
|
674
|
+
pending.delete(data.rootCallId)
|
|
675
|
+
recovered.add(previous)
|
|
676
|
+
recovered.add(entry)
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (pending.size) refuse()
|
|
680
|
+
const changedSeqs = new Set([...recovered].map(entry => entry.row.seq))
|
|
681
|
+
for (const { row } of entries.slice(1)) {
|
|
682
|
+
for (const range of row.sourceEventSeqs ?? []) {
|
|
683
|
+
if (typeof range === 'number' ? changedSeqs.has(range) : Array.isArray(range) && [...changedSeqs].some(seq => seq >= range[0] && seq <= range[1])) refuse()
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const text = entries.map(entry => recovered.has(entry)
|
|
687
|
+
? JSON.stringify({ type: 'dsh-mnemon/recovered-idle-probe', seq: entry.row.seq, time: entry.row.time, ignorable: true,
|
|
688
|
+
data: { originalType: entry.row.type, originalData: entry.row.data } }) + '\n'
|
|
689
|
+
: entry.line).join('')
|
|
690
|
+
const plain = Buffer.from(text)
|
|
691
|
+
if (plain.length > maximumBytes) throw new Error('Repaired Session exceeds the 128 MiB repair limit; no output written.')
|
|
692
|
+
const result = recovered.size === 0 ? original : compressed ? encodeFrames(text) : plain
|
|
693
|
+
if (result.length > maximumBytes) throw new Error('Repaired Session exceeds the 128 MiB repair limit; no output written.')
|
|
694
|
+
if (output !== undefined) await writeRepairCopy(input, output, result)
|
|
695
|
+
return { mode: output === undefined ? 'preview' : 'copy', format: 4, encoding: compressed ? 'zstd' : 'jsonl',
|
|
696
|
+
recoveredIdleProbeEvents: recovered.size, inputSha256: sha256(original), outputSha256: sha256(result),
|
|
697
|
+
originalPreserved: true, migrationValidated: false, blockers: [] }
|
|
698
|
+
}
|
|
699
|
+
|
|
617
700
|
export async function repairLegacySession(input, output) {
|
|
618
701
|
const original = await readBounded(input)
|
|
619
702
|
const compressed = original.subarray(0, 4).equals(Buffer.from([0x28, 0xb5, 0x2f, 0xfd]))
|
|
@@ -627,6 +710,7 @@ export async function repairLegacySession(input, output) {
|
|
|
627
710
|
const entries = lines.map((line, index) => {
|
|
628
711
|
try { return { line, index, row: JSON.parse(line) } } catch { throw new Error(`Invalid JSON at line ${index + 1}; no output written.`) }
|
|
629
712
|
})
|
|
713
|
+
if (entries[0]?.row?.version === 4) return repairIdleProbeSession(entries, original, compressed, input, output)
|
|
630
714
|
const recovery = recoverRecordedToolIdentities(entries)
|
|
631
715
|
let changes = 0
|
|
632
716
|
let descriptors = 0
|
|
@@ -681,15 +765,7 @@ export async function repairLegacySession(input, output) {
|
|
|
681
765
|
if (result.length > maximumBytes) throw new Error('Repaired Session exceeds the 128 MiB repair limit; no output written.')
|
|
682
766
|
const blockers = diagnostics.report()
|
|
683
767
|
if (output !== undefined && blockers.length === 0) {
|
|
684
|
-
|
|
685
|
-
const temporary = await mkdtemp(join(dirname(resolve(output)), '.mnemon-repair-'))
|
|
686
|
-
try {
|
|
687
|
-
const path = join(temporary, 'session')
|
|
688
|
-
const file = await open(path, 'wx', 0o600)
|
|
689
|
-
try { await file.writeFile(result); await file.sync() } finally { await file.close() }
|
|
690
|
-
// Publish exclusively, including when output is a symlink or hard link.
|
|
691
|
-
await link(path, output)
|
|
692
|
-
} finally { await rm(temporary, { recursive: true, force: true }) }
|
|
768
|
+
await writeRepairCopy(input, output, result)
|
|
693
769
|
}
|
|
694
770
|
return {
|
|
695
771
|
mode: output === undefined ? 'preview' : blockers.length === 0 ? 'copy' : 'refused',
|
|
@@ -712,7 +788,7 @@ export async function repairLegacySession(input, output) {
|
|
|
712
788
|
}
|
|
713
789
|
|
|
714
790
|
async function main(args) {
|
|
715
|
-
const usage = 'Usage: dsh-mnemon-repair-session --input FILE [--output NEW_FILE]\nWithout --output, preview the three known Mnemon v0 summary repairs, exact compatible v2 subagent descriptors, packed deltas with empty IDs/names, property-preserving null-to-empty delta names and proven DeepSeek chains with a previously recorded provider ID. Unsupported descriptor/tool shapes are reported with exit status 1 and prevent output. DSH must still validate the repaired copy. Stop DSH and work on a backup copy. The input and existing output files are never overwritten.'
|
|
791
|
+
const usage = 'Usage: dsh-mnemon-repair-session --input FILE [--output NEW_FILE]\nWithout --output, preview recognized v4 idle-review probe audit recovery or the three known Mnemon v0 summary repairs, exact compatible v2 subagent descriptors, packed deltas with empty IDs/names, property-preserving null-to-empty delta names and proven DeepSeek chains with a previously recorded provider ID. Unsupported descriptor/tool shapes are reported with exit status 1 and prevent output. DSH must still validate the repaired copy. Stop DSH and work on a backup copy. The input and existing output files are never overwritten.'
|
|
716
792
|
if (args.length === 1 && args[0] === '--help') { console.log(usage); return }
|
|
717
793
|
const options = new Map()
|
|
718
794
|
for (let index = 0; index < args.length; index += 2) {
|
package/lib/bundle.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as prepareStarterResolution } from "./starter-resolution-
|
|
1
|
+
import { t as prepareStarterResolution } from "./starter-resolution-C3yJawlA.js";
|
|
2
2
|
//#region src/bundle.ts
|
|
3
3
|
const groupKey = Symbol.for("cordis.group");
|
|
4
4
|
const init = Symbol.for("cordis.init");
|
package/lib/client.js
CHANGED
|
@@ -316,6 +316,7 @@ window.__ModuleLoader__.load({
|
|
|
316
316
|
evolution: false,
|
|
317
317
|
allowCreate: true,
|
|
318
318
|
allowUpdate: true,
|
|
319
|
+
targetScope: "user",
|
|
319
320
|
autoBacktest: true,
|
|
320
321
|
hints: true,
|
|
321
322
|
sidebar: false,
|
|
@@ -3054,7 +3055,7 @@ window.__ModuleLoader__.load({
|
|
|
3054
3055
|
"config.syncAuthorDefault": "默认",
|
|
3055
3056
|
"config.syncAuthorMachine": "本机 Git",
|
|
3056
3057
|
"config.syncAuthorCustom": "自定义",
|
|
3057
|
-
"config.syncAuthorDefaultNote": "
|
|
3058
|
+
"config.syncAuthorDefaultNote": "优先使用本机 Git 身份;没有配置时以 {identity} 提交",
|
|
3058
3059
|
"config.syncAuthorMachineNote": "使用这台电脑的 Git 身份;没有配置时用默认身份",
|
|
3059
3060
|
"config.syncAuthorName": "姓名",
|
|
3060
3061
|
"config.syncAuthorEmail": "邮箱",
|
|
@@ -3130,6 +3131,11 @@ window.__ModuleLoader__.load({
|
|
|
3130
3131
|
"skills.allowCreateHint": "允许复盘提出新技能;已有建议仍可手动处理",
|
|
3131
3132
|
"skills.allowUpdate": "改进已有技能",
|
|
3132
3133
|
"skills.allowUpdateHint": "允许复盘提出已有技能的改进;两类建议均关闭时暂停复盘",
|
|
3134
|
+
"skills.targetScope": "新技能写入位置",
|
|
3135
|
+
"skills.targetScopeHint": "默认写入用户技能;跟随存储范围时,workspace / workspaces 写入项目,其余写入用户。已有建议和草案保留目标,改进写回原技能;采纳时可手动选择。",
|
|
3136
|
+
"skills.targetScope.user": "用户",
|
|
3137
|
+
"skills.targetScope.project": "项目",
|
|
3138
|
+
"skills.targetScope.storage": "跟随存储范围",
|
|
3133
3139
|
"skills.evolutionHint": "后台复盘并建议改进技能;会创建落盘的任务及子代理会话,Mnemon 不自动删除这些记录",
|
|
3134
3140
|
"skills.autoBacktest": "草案自动回测",
|
|
3135
3141
|
"skills.autoBacktestHint": "草案写好后,用测试用例与当前版本对比",
|
|
@@ -3864,7 +3870,7 @@ window.__ModuleLoader__.load({
|
|
|
3864
3870
|
"config.syncAuthorDefault": "Default",
|
|
3865
3871
|
"config.syncAuthorMachine": "This computer's Git",
|
|
3866
3872
|
"config.syncAuthorCustom": "Custom",
|
|
3867
|
-
"config.syncAuthorDefaultNote": "
|
|
3873
|
+
"config.syncAuthorDefaultNote": "Uses this computer's Git identity; falls back to {identity} when unconfigured",
|
|
3868
3874
|
"config.syncAuthorMachineNote": "Uses this computer's Git identity, else the default one",
|
|
3869
3875
|
"config.syncAuthorName": "Name",
|
|
3870
3876
|
"config.syncAuthorEmail": "Email",
|
|
@@ -3940,6 +3946,11 @@ window.__ModuleLoader__.load({
|
|
|
3940
3946
|
"skills.allowCreateHint": "Allow reviews to propose new skills; existing suggestions can still be handled manually",
|
|
3941
3947
|
"skills.allowUpdate": "Suggest skill improvements",
|
|
3942
3948
|
"skills.allowUpdateHint": "Allow reviews to improve existing skills; reviews pause when both suggestion types are off",
|
|
3949
|
+
"skills.targetScope": "New skill destination",
|
|
3950
|
+
"skills.targetScopeHint": "Defaults to user skills. Follow storage uses the project for workspace / workspaces, otherwise the user. Existing proposals keep their destination; improvements keep their original file. You can choose again at adoption.",
|
|
3951
|
+
"skills.targetScope.user": "User",
|
|
3952
|
+
"skills.targetScope.project": "Project",
|
|
3953
|
+
"skills.targetScope.storage": "Follow storage",
|
|
3943
3954
|
"skills.evolutionHint": "Review conversations and suggest skills. Task and subagent sessions are saved locally; Mnemon does not automatically delete them.",
|
|
3944
3955
|
"skills.autoBacktest": "Backtest drafts",
|
|
3945
3956
|
"skills.autoBacktestHint": "Compare a new draft with the current version on its test cases",
|
|
@@ -8519,8 +8530,8 @@ window.__ModuleLoader__.load({
|
|
|
8519
8530
|
/**
|
|
8520
8531
|
* What this computer is called, where on the branch the memory goes and whose name
|
|
8521
8532
|
* its commits carry. Each field shows the value in use; the computer's name is the
|
|
8522
|
-
* host name or a typed one
|
|
8523
|
-
*
|
|
8533
|
+
* host name or a typed one. The default author follows this computer's Git
|
|
8534
|
+
* identity with a placeholder fallback; a custom author overrides it.
|
|
8524
8535
|
*/
|
|
8525
8536
|
function AdvancedSettings({ client, status, disabled, t, onSaved }) {
|
|
8526
8537
|
const [open, setOpen] = (0, react.useState)(false);
|
|
@@ -12028,8 +12039,8 @@ window.__ModuleLoader__.load({
|
|
|
12028
12039
|
}
|
|
12029
12040
|
/**
|
|
12030
12041
|
* 技能管理's settings on its page under Plugins. Switches apply at once; the
|
|
12031
|
-
* review parameters wait
|
|
12032
|
-
*
|
|
12042
|
+
* destination and folded review parameters wait for their own Apply. The
|
|
12043
|
+
* destination also applies to manual drafts while evolution is off.
|
|
12033
12044
|
*/
|
|
12034
12045
|
function SkillsSettings(props) {
|
|
12035
12046
|
const { scope, page, t } = props;
|
|
@@ -12044,6 +12055,9 @@ window.__ModuleLoader__.load({
|
|
|
12044
12055
|
const autoBacktest = useLive(saved.autoBacktest, write("autoBacktest"));
|
|
12045
12056
|
const hints = useLive(saved.hints, write("hints"));
|
|
12046
12057
|
const sidebar = useLive(saved.sidebar, write("sidebar"));
|
|
12058
|
+
const destination = useStaged({ targetScope: saved.targetScope }, async (draft) => {
|
|
12059
|
+
await scope.mutate([set("targetScope", draft.targetScope)]);
|
|
12060
|
+
});
|
|
12047
12061
|
const parameters = useStaged(parametersOf(saved), async (draft) => {
|
|
12048
12062
|
const changed = PARAMETERS.filter((field) => parameterValue(field, draft[field.key]) !== saved[field.key]);
|
|
12049
12063
|
if (changed.length > 0) await scope.mutate(changed.map((field) => set(field.key, parameterValue(field, draft[field.key]))));
|
|
@@ -12080,6 +12094,35 @@ window.__ModuleLoader__.load({
|
|
|
12080
12094
|
disabled,
|
|
12081
12095
|
onChange: allowUpdate.set
|
|
12082
12096
|
}),
|
|
12097
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectRow, {
|
|
12098
|
+
id: "mnemon-skills-target",
|
|
12099
|
+
label: t("skills.targetScope"),
|
|
12100
|
+
hint: t("skills.targetScopeHint"),
|
|
12101
|
+
value: destination.draft.targetScope,
|
|
12102
|
+
options: [
|
|
12103
|
+
"user",
|
|
12104
|
+
"project",
|
|
12105
|
+
"storage"
|
|
12106
|
+
].map((value) => ({
|
|
12107
|
+
value,
|
|
12108
|
+
label: t(`skills.targetScope.${value}`)
|
|
12109
|
+
})),
|
|
12110
|
+
disabled: disabled || destination.saving,
|
|
12111
|
+
onChange: (targetScope) => destination.edit({ targetScope })
|
|
12112
|
+
}),
|
|
12113
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelActions, {
|
|
12114
|
+
dirty: destination.dirty,
|
|
12115
|
+
saving: destination.saving,
|
|
12116
|
+
applied: destination.applied,
|
|
12117
|
+
failed: destination.failed,
|
|
12118
|
+
disabled,
|
|
12119
|
+
invalid: null,
|
|
12120
|
+
t,
|
|
12121
|
+
onDiscard: destination.discard,
|
|
12122
|
+
onApply: () => {
|
|
12123
|
+
destination.apply();
|
|
12124
|
+
}
|
|
12125
|
+
}),
|
|
12083
12126
|
evolution.value && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
|
|
12084
12127
|
id: "mnemon-skills-backtest",
|
|
12085
12128
|
label: t("skills.autoBacktest"),
|