@jjh32456/dsh-longlive 1.6.7 → 1.8.0
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/CHANGELOG.md +70 -1508
- package/LICENSE +1 -1
- package/QUICKSTART.md +34 -13
- package/README.en.md +18 -7
- package/README.md +34 -9
- package/SPEC.md +5 -5
- package/cordis.patch.yml +23 -9
- package/docs/ANSWER-GUARD.md +92 -0
- package/docs/ARCHITECTURE.md +4 -3
- package/docs/AUTO-RECALL-WIRING.md +1 -1
- package/docs/AUTO-RECALL.md +15 -9
- package/docs/CONFIG.md +168 -35
- package/docs/ERRORS.md +17 -5
- package/docs/FAQ.md +7 -7
- package/docs/FEATURES.md +96 -28
- package/docs/MIGRATION-1.6.0.md +2 -2
- package/docs/OPERATIONS.md +6 -6
- package/docs/PANEL-INTEGRATION.md +6 -0
- package/docs/PRIVACY.md +88 -0
- package/docs/QUICKSTART.md +5 -5
- package/docs/README.en.md +4 -2
- package/docs/RESIDENT-WATCHER.md +1 -1
- package/docs/SESSION-WATCH.md +6 -6
- package/docs/TROUBLESHOOTING.md +4 -4
- package/package.json +47 -117
- package/runbook.md +2 -2
- package/scripts/backfill-auto-cards.mjs +98 -0
- package/scripts/install-surgery-task.ps1 +5 -2
- package/scripts/lib/session-watch-fixture.mjs +1 -1
- package/scripts/longlive-doctor.mjs +6 -0
- package/scripts/migrate-180.mjs +163 -0
- package/scripts/recall-index-build.mjs +128 -0
- package/scripts/session-watch-drill.mjs +1 -1
- package/scripts/surgery-run.cmd +1 -1
- package/scripts/unattended-surgery.ps1 +5 -2
- package/scripts/verify-card-anchors.mjs +138 -0
- package/src/actions/audit.js +125 -0
- package/src/actions/evidence.js +122 -0
- package/src/actions/fs.js +162 -0
- package/src/activity/index.js +760 -0
- package/src/activity/model.js +351 -0
- package/src/activity/store.js +644 -0
- package/src/activity/thinking.js +176 -0
- package/src/adapters/constants.js +11 -0
- package/src/adapters/engram/data/aliases.json +51 -0
- package/src/adapters/engram/data/policy.json +8 -0
- package/src/adapters/engram/data/routing-rules.json +546 -0
- package/src/adapters/engram/index.js +235 -0
- package/src/adapters/registry.js +315 -0
- package/src/answer/audit.js +193 -0
- package/src/answer/evidence.js +30 -0
- package/src/answer/guard.js +1370 -0
- package/src/answer/index.js +1067 -0
- package/src/answer/query.js +111 -0
- package/src/answer/second-hop.mjs +101 -0
- package/src/auto-truncate.js +1 -1
- package/src/config.js +171 -47
- package/src/deps.js +6 -4
- package/src/features.js +182 -6
- package/src/heartbeat.js +156 -23
- package/src/identity/handover.js +52 -1
- package/src/identity/index.js +13 -5
- package/src/identity/inject-position.js +2 -2
- package/src/index.js +97 -7
- package/src/lifecycle/gap-clocks.js +328 -0
- package/src/lifecycle/index.js +269 -0
- package/src/live.js +22 -2
- package/src/memory/activity-capture.js +229 -0
- package/src/memory/anchors.js +194 -0
- package/src/memory/audit-records.js +222 -0
- package/src/memory/auto-cards.js +401 -0
- package/src/memory/capture.js +442 -0
- package/src/memory/card-anchors.js +203 -0
- package/src/memory/conflicts.js +106 -0
- package/src/memory/decay.js +311 -0
- package/src/memory/distill.js +471 -0
- package/src/memory/event-index.js +917 -0
- package/src/memory/evidence-chain.js +295 -0
- package/src/memory/evidence.js +145 -0
- package/src/memory/filter-runner.js +1 -1
- package/src/memory/filter.js +5 -1
- package/src/memory/forget-v2.js +410 -0
- package/src/memory/forget.js +71 -0
- package/src/memory/frames-activation.js +247 -0
- package/src/memory/fts-query.js +174 -0
- package/src/memory/governance.js +604 -0
- package/src/memory/index.js +1 -0
- package/src/memory/l2-gate.js +169 -0
- package/src/memory/lock.js +121 -0
- package/src/memory/migrate.js +139 -0
- package/src/memory/recall-v2.js +223 -0
- package/src/memory/recall.js +65 -2
- package/src/memory/records.js +425 -0
- package/src/memory/retention.js +128 -0
- package/src/memory/review.js +124 -0
- package/src/memory/trust.js +88 -0
- package/src/memory/version-chain.js +264 -0
- package/src/memory/wal.js +137 -0
- package/src/memory/working-frame.js +516 -0
- package/src/metrics/classify.mjs +5 -3
- package/src/metrics/index.mjs +21 -1
- package/src/metrics/prestep-latency.mjs +300 -0
- package/src/metrics/store.mjs +25 -0
- package/src/policy-profile.js +18 -3
- package/src/recall/anaphora.mjs +87 -0
- package/src/recall/cards.mjs +7 -0
- package/src/recall/embedding.mjs +8 -8
- package/src/recall/entities.mjs +112 -0
- package/src/recall/index.js +235 -19
- package/src/recall/inject-order.mjs +10 -8
- package/src/recall/privacy.mjs +105 -16
- package/src/recall/recall-core.mjs +116 -10
- package/src/recall/shared-visibility.js +302 -0
- package/src/recall/time-normalizer.mjs +196 -0
- package/src/role/activation.js +191 -0
- package/src/role/attention.js +318 -0
- package/src/role/base.js +36 -0
- package/src/role/channels.js +423 -0
- package/src/role/index.js +425 -0
- package/src/role/interfaces.js +115 -0
- package/src/role/personality.js +370 -0
- package/src/role/readers.js +151 -0
- package/src/role/relationships.js +477 -0
- package/src/role/store.js +202 -0
- package/src/role/writeprotect.js +373 -0
- package/src/router/aliases.json +176 -25
- package/src/router/background/agent-runner.mjs +1 -1
- package/src/router/background/anchor.mjs +1 -1
- package/src/router/background/audit.mjs +2 -2
- package/src/router/background/budget-ledger.mjs +2 -2
- package/src/router/background/host-sim.mjs +1 -1
- package/src/router/background/memory.mjs +4 -4
- package/src/router/background/queue.mjs +1 -1
- package/src/router/background/retry.mjs +1 -1
- package/src/router/background/router.mjs +1 -1
- package/src/router/background/stream.mjs +1 -1
- package/src/router/background/tool-policy.mjs +1 -1
- package/src/router/design-headings.json +5 -5
- package/src/router/face/config.mjs +1 -1
- package/src/router/face/identity.mjs +3 -3
- package/src/router/judge.mjs +8 -7
- package/src/router/policies/generic.json +21 -4
- package/src/router/route.js +109 -22
- package/src/router/router.mjs +10 -6
- package/src/router/routing-rules.json +260 -445
- package/src/router/run-sim.mjs +8 -8
- package/src/router/runtime.js +23 -12
- package/src/router/service/board.mjs +95 -15
- package/src/router/service/config.mjs +5 -5
- package/src/router/service/context.mjs +1 -1
- package/src/router/service/hint.mjs +14 -0
- package/src/router/service/queue.mjs +1 -1
- package/src/router/service/service.mjs +27 -11
- package/src/router/shadow.mjs +4 -3
- package/src/surgery-recovery.js +2 -2
- package/src/tool-output.js +2 -2
- package/src/tools.js +39 -7
- package/src/truncate-visible.js +2 -2
- package/src/ui/actions.js +1312 -0
- package/src/ui/panel-api.js +367 -0
- package/src/ui/panel-auth.js +251 -0
- package/src/ui/panel-integrity.js +243 -0
- package/src/ui/routes-readonly.js +695 -0
- package/src/ui/routes.js +3 -0
- package/docs/DEV.md +0 -279
- package/docs/PRESENCE-BUS-WIRING.md +0 -491
- package/docs/PRESENCE-BUS.md +0 -694
- package/docs/SURGERY.md +0 -186
- package/scripts/mark-core-cards.mjs +0 -289
- package/scripts/pack.mjs +0 -392
- package/scripts/router-proposals.mjs +0 -386
- package/src/router/fixtures/M1.json +0 -16
- package/src/router/fixtures/M2.json +0 -16
- package/src/router/fixtures/M3.json +0 -20
- package/src/router/fixtures/M4.json +0 -20
- package/src/router/fixtures/M5.json +0 -45
- package/src/router/fixtures/M6.json +0 -53
- package/src/router/fixtures/M7.json +0 -46
- package/src/router/fixtures/S1.json +0 -20
- package/src/router/fixtures/S2.json +0 -23
- package/src/router/fixtures/S3.json +0 -20
- package/src/router/fixtures/S4.json +0 -34
- package/src/router/fixtures/S5.json +0 -43
- package/src/router/fixtures/S6.json +0 -47
- package/src/router/fixtures/T1.json +0 -23
- package/src/router/fixtures/T2.json +0 -16
- package/src/router/fixtures/T3.json +0 -19
- package/src/router/fixtures/T4.json +0 -16
- package/src/router/fixtures/X1.json +0 -17
- package/src/router/fixtures/X2.json +0 -20
- package/src/router/gen-design-headings.mjs +0 -107
- package/src/router/policies/owner-rulings-2026-11-03.json +0 -62
- package/test/auto-recall.test.mjs +0 -474
- package/test/auto-truncate.test.mjs +0 -931
- package/test/autobind.test.mjs +0 -385
- package/test/bus.test.mjs +0 -1572
- package/test/cli-guard.test.mjs +0 -198
- package/test/conclusion.test.mjs +0 -408
- package/test/config-schema.test.mjs +0 -204
- package/test/d6-output.test.mjs +0 -194
- package/test/d6.test.mjs +0 -395
- package/test/deps.test.mjs +0 -342
- package/test/doctor-setup.test.mjs +0 -297
- package/test/embedding-recall.test.mjs +0 -347
- package/test/feature-flags.test.mjs +0 -568
- package/test/filter-shadow.test.mjs +0 -170
- package/test/fixtures/baseline-0.8.0-registration.json +0 -556
- package/test/fixtures/d6-header-tools.json +0 -1
- package/test/fixtures/lock-child.mjs +0 -67
- package/test/fixtures/pre-step-container.mjs +0 -100
- package/test/fixtures/privacy-memory/topics/pii-unmarked.md +0 -9
- package/test/fixtures/privacy-memory/topics/secret-local-only.md +0 -11
- package/test/fixtures/profile-session-longlive-config.json +0 -20
- package/test/fixtures/proposal-cas-child.mjs +0 -66
- package/test/host-engine-status.test.mjs +0 -307
- package/test/hot-truncate.test.mjs +0 -1349
- package/test/identity-card-marker.test.mjs +0 -307
- package/test/identity-router/run.mjs +0 -721
- package/test/integration-pre-step.test.mjs +0 -553
- package/test/live.test.mjs +0 -1020
- package/test/maintenance-schedule.test.mjs +0 -538
- package/test/memory.test.mjs +0 -2828
- package/test/metrics.test.mjs +0 -478
- package/test/orchestrator.test.mjs +0 -266
- package/test/p1_probe.mjs +0 -97
- package/test/packaging.test.mjs +0 -182
- package/test/portability.test.mjs +0 -343
- package/test/rc4-regressions.test.mjs +0 -562
- package/test/rc5-regressions.test.mjs +0 -197
- package/test/rc6-chinese.test.mjs +0 -394
- package/test/router-1100/apply-off.test.mjs +0 -175
- package/test/router-1100/budget-reconcile.test.mjs +0 -215
- package/test/router-1100/config-context.test.mjs +0 -211
- package/test/router-1100/helpers.mjs +0 -129
- package/test/router-1100/hint-envelope.test.mjs +0 -150
- package/test/router-1100/lock-crossproc.test.mjs +0 -206
- package/test/router-1100/proposal-cas-crossproc.test.mjs +0 -184
- package/test/router-1100/router-apply.test.mjs +0 -372
- package/test/router-1100/router-fixb.test.mjs +0 -602
- package/test/router-1100/router-proposals.test.mjs +0 -405
- package/test/router-1100/router-queue-lease-wal.test.mjs +0 -355
- package/test/router-1100/router-security-cr.test.mjs +0 -333
- package/test/router-1100/service-lifecycle.test.mjs +0 -220
- package/test/router-1100/tools-contract.test.mjs +0 -164
- package/test/router-1100/turn-reflect-tick.test.mjs +0 -89
- package/test/router-1100/v6-contract.test.mjs +0 -158
- package/test/router-1100/v6b-contract.test.mjs +0 -293
- package/test/router-1100/v6c-contract.test.mjs +0 -249
- package/test/router-1100/wal-rotation.test.mjs +0 -375
- package/test/router-1100/wp1-config.test.mjs +0 -170
- package/test/router-1100/wp1-default-off.test.mjs +0 -77
- package/test/router-1100/wp1-model-status.test.mjs +0 -90
- package/test/router-1100/wp1-status.test.mjs +0 -220
- package/test/router-1100/wp1-tools.test.mjs +0 -146
- package/test/router-1100/wp5-cli.test.mjs +0 -240
- package/test/router-1100/wp5-model-auto.test.mjs +0 -418
- package/test/router-1100/wp6-merge.test.mjs +0 -386
- package/test/router-1100/wp6-wake.test.mjs +0 -502
- package/test/router-background/audit.test.mjs +0 -37
- package/test/router-background/budget-ledger.test.mjs +0 -330
- package/test/router-background/cross-process.test.mjs +0 -222
- package/test/router-background/fixtures/cross-child.mjs +0 -85
- package/test/router-background/fixtures/real-shapes.mjs +0 -239
- package/test/router-background/router.test.mjs +0 -1044
- package/test/router-background/stream-retry.test.mjs +0 -358
- package/test/router-background/tool-agent.test.mjs +0 -384
- package/test/router-face/bridge.test.mjs +0 -169
- package/test/router-face/face.test.mjs +0 -316
- package/test/router-failclosed.test.mjs +0 -466
- package/test/router-identity.test.mjs +0 -384
- package/test/router-l1-l2.contract.test.mjs +0 -1075
- package/test/router-memory-tools.test.mjs +0 -262
- package/test/router-policy.test.mjs +0 -225
- package/test/router-runtime.test.mjs +0 -438
- package/test/router-shadow.test.mjs +0 -287
- package/test/router.test.mjs +0 -903
- package/test/run-rc.mjs +0 -299
- package/test/run.mjs +0 -2469
- package/test/safety-defaults.test.mjs +0 -179
- package/test/seeded-load.test.mjs +0 -436
- package/test/session-original.test.mjs +0 -338
- package/test/surgery-recovery.test.mjs +0 -936
- package/test/t7_drive.mjs +0 -151
- package/test/tool-output.test.mjs +0 -393
- package/test/tool-schema.test.mjs +0 -177
- package/test/tools_contract.test.mjs +0 -716
- package/test/truncate-visible.test.mjs +0 -1150
- package/test/ui-flags.test.mjs +0 -477
- package/test/v4-hardening.test.mjs +0 -427
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
// Portability acceptance tests (0.7.3).
|
|
2
|
-
//
|
|
3
|
-
// These cases pin the behaviour that made the package machine-specific before:
|
|
4
|
-
// PORT1 a missing memory root resolves to <home>/longlive-memory and is created
|
|
5
|
-
// PORT2 explicit config / alias / environment precedence is one shared chain
|
|
6
|
-
// PORT3 strict mode still refuses to guess, and a configured-but-missing root
|
|
7
|
-
// is never created silently
|
|
8
|
-
// PORT4 the host version check parses semver, reads engines.dsh, and degrades
|
|
9
|
-
// with a readable warning instead of throwing
|
|
10
|
-
// PORT5 every published file is free of developer-machine paths
|
|
11
|
-
// PORT6 the identity-consent rule names no assistant: consent belongs to the
|
|
12
|
-
// assistant of the current session, whoever that assistant is
|
|
13
|
-
//
|
|
14
|
-
// Nothing here writes inside the package tree (P6 checks that afterwards).
|
|
15
|
-
|
|
16
|
-
import fs from 'node:fs';
|
|
17
|
-
import os from 'node:os';
|
|
18
|
-
import path from 'node:path';
|
|
19
|
-
import { fileURLToPath } from 'node:url';
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
MEMORY_ROOT_ENV,
|
|
23
|
-
LEGACY_MEMORY_ROOT_ENV,
|
|
24
|
-
DEFAULT_MEMORY_DIRNAME,
|
|
25
|
-
memoryRootFromConfig,
|
|
26
|
-
memoryRootSetting,
|
|
27
|
-
defaultMemoryRoot,
|
|
28
|
-
} from '../src/memory/paths.js';
|
|
29
|
-
import { memRoot, TOOLS } from '../src/tools.js';
|
|
30
|
-
import {
|
|
31
|
-
HOST_VERSION_ENV,
|
|
32
|
-
DEFAULT_HOST_RANGE,
|
|
33
|
-
parseVersion,
|
|
34
|
-
satisfies,
|
|
35
|
-
declaredHostRange,
|
|
36
|
-
checkHostCompatibility,
|
|
37
|
-
} from '../src/host-version.js';
|
|
38
|
-
import { VERSION } from '../src/version.js';
|
|
39
|
-
|
|
40
|
-
const PACKAGE_ROOT = path.dirname(fileURLToPath(new URL('../package.json', import.meta.url)));
|
|
41
|
-
|
|
42
|
-
function readIf(file) {
|
|
43
|
-
try {
|
|
44
|
-
return fs.readFileSync(file, 'utf8');
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function walk(files, file) {
|
|
51
|
-
const text = readIf(file);
|
|
52
|
-
if (text !== null) files.push({ file, text });
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// package.json `files` ships src/router/*.{js,mjs} plus every nested JSON, and
|
|
56
|
-
// only top-level .js elsewhere under src. Walk src recursively and mirror that
|
|
57
|
-
// shape, so a new subdirectory (src/router was the first) cannot fall out of
|
|
58
|
-
// the scan again.
|
|
59
|
-
const SRC_SHIPPED = [
|
|
60
|
-
/^src\/[^/]+\.js$/,
|
|
61
|
-
/^src\/memory\/[^/]+\.js$/,
|
|
62
|
-
/^src\/router\/[^/]+\.(?:js|mjs)$/,
|
|
63
|
-
/^src\/router\/(?:[^/]+\/)*[^/]+\.json$/,
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
function collectSrc(files, dir) {
|
|
67
|
-
for (const name of fs.readdirSync(dir).sort()) {
|
|
68
|
-
const abs = path.join(dir, name);
|
|
69
|
-
if (fs.statSync(abs).isDirectory()) {
|
|
70
|
-
collectSrc(files, abs);
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
const rel = path.relative(PACKAGE_ROOT, abs).split(path.sep).join('/');
|
|
74
|
-
if (SRC_SHIPPED.some((re) => re.test(rel))) walk(files, abs);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function publishedFiles() {
|
|
79
|
-
const files = [];
|
|
80
|
-
for (const name of [
|
|
81
|
-
'package.json', 'cordis.patch.yml', 'README.md', 'README.en.md', 'SPEC.md',
|
|
82
|
-
'runbook.md', 'QUICKSTART.md', 'CHANGELOG.md', 'docs/ERRORS.md',
|
|
83
|
-
]) walk(files, path.join(PACKAGE_ROOT, name));
|
|
84
|
-
for (const dir of ['scripts', 'test', 'locale']) {
|
|
85
|
-
const abs = path.join(PACKAGE_ROOT, dir);
|
|
86
|
-
if (!fs.existsSync(abs)) continue;
|
|
87
|
-
for (const name of fs.readdirSync(abs)) {
|
|
88
|
-
const file = path.join(abs, name);
|
|
89
|
-
if (!fs.statSync(file).isFile()) continue;
|
|
90
|
-
// test ships .mjs too; locale ships JSON; scripts ship both.
|
|
91
|
-
const shipped = /\.m?js$/.test(name)
|
|
92
|
-
|| name.endsWith('.ps1')
|
|
93
|
-
|| /\.json$/.test(name);
|
|
94
|
-
if (shipped) walk(files, file);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const src = path.join(PACKAGE_ROOT, 'src');
|
|
98
|
-
if (fs.existsSync(src)) collectSrc(files, src);
|
|
99
|
-
return files;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Patterns are assembled from pieces so this scanner does not match its own
|
|
103
|
-
// source text (the file itself ships and is scanned like any other).
|
|
104
|
-
const DRIVE = '[A-Za-z]:';
|
|
105
|
-
const SEP = '[\\\\/]+';
|
|
106
|
-
const MACHINE_PATTERNS = [
|
|
107
|
-
{ name: 'developer profile path', re: new RegExp(`${DRIVE}${SEP}Users${SEP}Admin` + 'istrator', 'i') },
|
|
108
|
-
{ name: 'private toolbox path', re: new RegExp(`${DRIVE}${SEP}` + 'dshops', 'i') },
|
|
109
|
-
{ name: 'personal memory drive', re: new RegExp('(?<![A-Za-z0-9_])H:' + '\\\\', 'i') },
|
|
110
|
-
{ name: 'developer runtime drive', re: new RegExp('(?<![A-Za-z0-9_])G:' + '\\\\', 'i') },
|
|
111
|
-
{ name: 'ACE Studio install path', re: new RegExp('Program Files' + SEP + 'ACE ' + 'Studio', 'i') },
|
|
112
|
-
{ name: 'private research path', re: new RegExp('听澜' + SEP + 'research') },
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
// PORT6 scans the authored policy text (routing rules + judge/router sources +
|
|
116
|
-
// the error catalogue) for a hardcoded assistant name. The consent holder is
|
|
117
|
-
// "the assistant of the current session" (consent.holder=assistant); a user's
|
|
118
|
-
// unilateral request is never consent, no matter what the assistant is called.
|
|
119
|
-
const PERSONA = '听澜';
|
|
120
|
-
const OPERATOR_NAME = '金嘉恒';
|
|
121
|
-
const PERSONA_RE = new RegExp(`${PERSONA}|${OPERATOR_NAME}`);
|
|
122
|
-
// Development-only absolute paths are not policy text: strip the path stem
|
|
123
|
-
// (`<persona>/research/...`) before looking for persona wording.
|
|
124
|
-
const DEV_PATH_RE = new RegExp(`${PERSONA}[\\\\/]+research`, 'g');
|
|
125
|
-
// An override audit records WHO overrode a rule; that is an example signature,
|
|
126
|
-
// not the subject of a policy.
|
|
127
|
-
const EXAMPLE_SIGNATURE_RE = /\bby:\s*'[^']*'/g;
|
|
128
|
-
// Skipped wholesale, each because its persona mentions are example data. Keep
|
|
129
|
-
// this list short: every other top-level src/router source is policy surface.
|
|
130
|
-
const EXAMPLE_DATA_FILES = new Map([
|
|
131
|
-
['src/router/design-headings.json',
|
|
132
|
-
'generated verbatim snapshot of the external design doc; quoting the ruling as written is the point'],
|
|
133
|
-
['src/router/run-sim.mjs',
|
|
134
|
-
'sandbox simulation driver; its literal names are example journal content and override signatures'],
|
|
135
|
-
]);
|
|
136
|
-
|
|
137
|
-
function policyTextFiles() {
|
|
138
|
-
const files = [];
|
|
139
|
-
const routerDir = path.join(PACKAGE_ROOT, 'src/router');
|
|
140
|
-
const collect = (dir, prefix) => {
|
|
141
|
-
for (const name of fs.readdirSync(dir).sort()) {
|
|
142
|
-
if (!/\.(?:json|m?js)$/.test(name)) continue;
|
|
143
|
-
const abs = path.join(dir, name);
|
|
144
|
-
if (!fs.statSync(abs).isFile()) continue;
|
|
145
|
-
const rel = `${prefix}/${name}`;
|
|
146
|
-
if (EXAMPLE_DATA_FILES.has(rel)) continue;
|
|
147
|
-
files.push({ rel, text: fs.readFileSync(abs, 'utf8') });
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
collect(routerDir, 'src/router');
|
|
151
|
-
// 策略 profile 是可配置的家规面:generic/owner 都必须保持可移植(无个人名/本机路径)。
|
|
152
|
-
const policiesDir = path.join(routerDir, 'policies');
|
|
153
|
-
if (fs.existsSync(policiesDir)) collect(policiesDir, 'src/router/policies');
|
|
154
|
-
files.push({
|
|
155
|
-
rel: 'docs/ERRORS.md',
|
|
156
|
-
text: fs.readFileSync(path.join(PACKAGE_ROOT, 'docs/ERRORS.md'), 'utf8'),
|
|
157
|
-
});
|
|
158
|
-
return files;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function runPortabilitySuite({ runCase, assert, TMP }) {
|
|
162
|
-
runCase('PORT1', '缺省记忆根:派生 <home>/longlive-memory、按需创建、memRoot 同源', () => {
|
|
163
|
-
const home = path.join(TMP, 'port-home-1');
|
|
164
|
-
fs.rmSync(home, { recursive: true, force: true });
|
|
165
|
-
const expected = path.join(home, DEFAULT_MEMORY_DIRNAME);
|
|
166
|
-
|
|
167
|
-
const setting = memoryRootSetting({ home }, {});
|
|
168
|
-
assert(setting.explicit === '', 'no explicit root expected');
|
|
169
|
-
assert(setting.source === 'default', `unexpected source: ${setting.source}`);
|
|
170
|
-
assert(setting.strict === false, 'strict must default off');
|
|
171
|
-
assert(defaultMemoryRoot({ home }, {}) === expected, 'derived root path mismatch');
|
|
172
|
-
|
|
173
|
-
const root = memoryRootFromConfig({ home }, {});
|
|
174
|
-
assert(root === expected, `memoryRootFromConfig returned ${root}`);
|
|
175
|
-
assert(fs.statSync(root).isDirectory(), 'derived root was not created');
|
|
176
|
-
|
|
177
|
-
assert(memRoot({ home }, {}) === root, 'session_wake and memory_* disagree on the root');
|
|
178
|
-
assert(fs.statSync(root).isDirectory(), 'memRoot did not keep the derived root');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
runCase('PORT2', '记忆根优先级:memoryRoot > memRoot > 新环境变量 > 旧环境变量', () => {
|
|
182
|
-
const a = path.join(TMP, 'port-root-a');
|
|
183
|
-
const b = path.join(TMP, 'port-root-b');
|
|
184
|
-
const c = path.join(TMP, 'port-root-c');
|
|
185
|
-
const d = path.join(TMP, 'port-root-d');
|
|
186
|
-
for (const dir of [a, b, c, d]) fs.mkdirSync(dir, { recursive: true });
|
|
187
|
-
|
|
188
|
-
assert(memoryRootFromConfig({ memoryRoot: a, memRoot: b }, {}) === a, 'memoryRoot must win');
|
|
189
|
-
assert(memoryRootFromConfig({ memRoot: b }, { [MEMORY_ROOT_ENV]: c, [LEGACY_MEMORY_ROOT_ENV]: d }) === b,
|
|
190
|
-
'memRoot alias must win over the environment');
|
|
191
|
-
assert(memoryRootFromConfig({}, { [MEMORY_ROOT_ENV]: c, [LEGACY_MEMORY_ROOT_ENV]: d }) === c,
|
|
192
|
-
'new environment variable must win over the legacy one');
|
|
193
|
-
assert(memoryRootFromConfig({}, { [LEGACY_MEMORY_ROOT_ENV]: d }) === d,
|
|
194
|
-
'legacy DSH_MEM must still be honoured');
|
|
195
|
-
assert(memRoot({ memRoot: b }, {}) === b, 'memRoot must honour the memoryRoot alias chain');
|
|
196
|
-
|
|
197
|
-
const setting = memoryRootSetting({}, { [MEMORY_ROOT_ENV]: c });
|
|
198
|
-
assert(setting.source === MEMORY_ROOT_ENV, `unexpected source ${setting.source}`);
|
|
199
|
-
assert(setting.explicit === c, 'setting.explicit mismatch');
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
runCase('PORT3', 'strict 模式与显式缺失:报错可操作,且绝不创建显式配置的路径', () => {
|
|
203
|
-
const home = path.join(TMP, 'port-home-3');
|
|
204
|
-
fs.rmSync(home, { recursive: true, force: true });
|
|
205
|
-
|
|
206
|
-
let strictError = null;
|
|
207
|
-
try {
|
|
208
|
-
memoryRootFromConfig({ home, strictMemoryRoot: true }, {});
|
|
209
|
-
} catch (error) {
|
|
210
|
-
strictError = error;
|
|
211
|
-
}
|
|
212
|
-
assert(strictError, 'strict mode must refuse to derive a root');
|
|
213
|
-
assert(strictError.code === 'E_MEMORY_ROOT_UNSET', `unexpected code ${strictError.code}`);
|
|
214
|
-
assert(/memoryRoot/.test(strictError.message), 'strict message must name memoryRoot');
|
|
215
|
-
assert(strictError.message.includes(MEMORY_ROOT_ENV), 'strict message must name the environment variable');
|
|
216
|
-
assert(strictError.message.includes('longlive-memory'), 'strict message must name the derived default');
|
|
217
|
-
|
|
218
|
-
const missing = path.join(TMP, 'port-explicit-missing');
|
|
219
|
-
let missingError = null;
|
|
220
|
-
try {
|
|
221
|
-
memoryRootFromConfig({ memoryRoot: missing }, {});
|
|
222
|
-
} catch (error) {
|
|
223
|
-
missingError = error;
|
|
224
|
-
}
|
|
225
|
-
assert(missingError, 'a configured root that does not exist must be reported');
|
|
226
|
-
assert(missingError.code === 'E_MEMORY_ROOT_MISSING', `unexpected code ${missingError.code}`);
|
|
227
|
-
assert(!fs.existsSync(missing), 'an explicitly configured path must never be created silently');
|
|
228
|
-
assert(/mkdir/.test(missingError.message), 'missing-root message must tell the user how to fix it');
|
|
229
|
-
|
|
230
|
-
const envMissing = path.join(TMP, 'port-env-missing');
|
|
231
|
-
let envError = null;
|
|
232
|
-
try {
|
|
233
|
-
memoryRootFromConfig({}, { [LEGACY_MEMORY_ROOT_ENV]: envMissing });
|
|
234
|
-
} catch (error) {
|
|
235
|
-
envError = error;
|
|
236
|
-
}
|
|
237
|
-
assert(envError && envError.code === 'E_MEMORY_ROOT_MISSING', 'legacy env root must be validated');
|
|
238
|
-
assert(!fs.existsSync(envMissing), 'environment root must never be created silently');
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
runCase('PORT4', '宿主版本自检:semver、engines.dsh 上界、未知不阻断、失配只降级', () => {
|
|
242
|
-
const parsed = parseVersion('v0.2.1-rc.5');
|
|
243
|
-
assert(parsed && parsed.major === 0 && parsed.minor === 2 && parsed.patch === 1, 'prerelease core must parse');
|
|
244
|
-
assert(satisfies('0.2.1-rc.5', DEFAULT_HOST_RANGE), 'the running host must satisfy the declared range');
|
|
245
|
-
assert(satisfies('0.2.0', DEFAULT_HOST_RANGE), 'lower bound is inclusive');
|
|
246
|
-
assert(!satisfies('0.1.9', DEFAULT_HOST_RANGE), 'older host must be refused');
|
|
247
|
-
assert(!satisfies('1.0.0', DEFAULT_HOST_RANGE), 'newer major must be refused');
|
|
248
|
-
assert(satisfies('0.7.2', '^0.7.0') && !satisfies('0.8.0', '^0.7.0'), 'caret on 0.x');
|
|
249
|
-
assert(satisfies('1.4.0', '>=1.0.0 || =0.9.9'), 'or-ranges');
|
|
250
|
-
|
|
251
|
-
const range = declaredHostRange();
|
|
252
|
-
assert(range === DEFAULT_HOST_RANGE, `package.json engines.dsh is ${range}`);
|
|
253
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
254
|
-
assert(pkg.engines.dsh === range, 'declared range must come from package.json');
|
|
255
|
-
|
|
256
|
-
const good = checkHostCompatibility({ version: '0.2.1-rc.5', range, pluginVersion: VERSION });
|
|
257
|
-
assert(good.ok === true && good.status === 'ok' && good.warning === null, 'matching host must pass');
|
|
258
|
-
|
|
259
|
-
const unknown = checkHostCompatibility({ range, pluginVersion: VERSION });
|
|
260
|
-
assert(unknown.ok === true && unknown.status === 'unknown', 'an unknown host must not be blocked');
|
|
261
|
-
assert(unknown.warning === null, 'an unknown host must not warn until it is known');
|
|
262
|
-
|
|
263
|
-
const bad = checkHostCompatibility({ version: '0.1.0', range, pluginVersion: VERSION });
|
|
264
|
-
assert(bad.ok === false && bad.status === 'mismatch', 'old host must be a mismatch');
|
|
265
|
-
assert(bad.warning.includes('0.1.0') && bad.warning.includes('engines.dsh'), 'warning must name both sides');
|
|
266
|
-
assert(/降级|degraded/i.test(bad.warning), 'warning must promise degraded loading, not a crash');
|
|
267
|
-
|
|
268
|
-
assert(typeof HOST_VERSION_ENV === 'string' && HOST_VERSION_ENV.startsWith('DSH_'), 'env name');
|
|
269
|
-
const oldDrive = 'H:' + '\\dsh-memory';
|
|
270
|
-
const wake = TOOLS.find((tool) => tool.name === 'session_wake');
|
|
271
|
-
assert(wake && !wake.description.includes(oldDrive), 'session_wake still advertises the old drive');
|
|
272
|
-
const lease = TOOLS.find((tool) => tool.name === 'lease_acquire');
|
|
273
|
-
assert(lease && !lease.description.includes(oldDrive), 'lease_acquire still advertises the old drive');
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
runCase('PORT5', '发布文件不含本机盘符、用户目录与私有目录名', () => {
|
|
277
|
-
for (const required of ['QUICKSTART.md', 'docs/ERRORS.md']) {
|
|
278
|
-
assert(fs.existsSync(path.join(PACKAGE_ROOT, required)), `${required} must ship`);
|
|
279
|
-
}
|
|
280
|
-
const files = publishedFiles();
|
|
281
|
-
const scanned = files.map(({ file }) => path.relative(PACKAGE_ROOT, file).split(path.sep).join('/'));
|
|
282
|
-
// A scan that silently skips a shipped subtree proves nothing: pin the
|
|
283
|
-
// files whose machine paths this case exists to catch.
|
|
284
|
-
for (const required of [
|
|
285
|
-
'src/router/router.mjs',
|
|
286
|
-
'src/router/design-headings.json',
|
|
287
|
-
'src/router/shadow.mjs',
|
|
288
|
-
'scripts/pack.mjs',
|
|
289
|
-
]) {
|
|
290
|
-
assert(scanned.includes(required), `PORT5 漏扫随包文件 ${required}`);
|
|
291
|
-
}
|
|
292
|
-
const hits = [];
|
|
293
|
-
for (const { file, text } of files) {
|
|
294
|
-
const lines = text.split(/\r?\n/);
|
|
295
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
296
|
-
for (const { name, re } of MACHINE_PATTERNS) {
|
|
297
|
-
if (re.test(lines[i])) hits.push(`${path.relative(PACKAGE_ROOT, file)}:${i + 1} (${name}) ${lines[i].trim().slice(0, 100)}`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
assert(hits.length === 0, `machine-specific paths in published files:\n${hits.join('\n')}`);
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
runCase('PORT6', '身份同意规则不写死某个 agent 的名字:同意权归当前会话的助手本人', () => {
|
|
305
|
-
for (const rel of EXAMPLE_DATA_FILES.keys()) {
|
|
306
|
-
assert(fs.existsSync(path.join(PACKAGE_ROOT, rel)), `${rel} 被列为示例数据但文件不存在`);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const files = policyTextFiles();
|
|
310
|
-
const scanned = files.map((file) => file.rel);
|
|
311
|
-
for (const required of [
|
|
312
|
-
'src/router/routing-rules.json',
|
|
313
|
-
'src/router/judge.mjs',
|
|
314
|
-
'src/router/route.js',
|
|
315
|
-
'docs/ERRORS.md',
|
|
316
|
-
]) {
|
|
317
|
-
assert(scanned.includes(required), `可移植性扫描漏了 ${required}`);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
const hits = [];
|
|
321
|
-
for (const { rel, text } of files) {
|
|
322
|
-
const lines = text.split(/\r?\n/);
|
|
323
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
324
|
-
const subject = lines[i]
|
|
325
|
-
.replace(DEV_PATH_RE, '<dev-path>')
|
|
326
|
-
.replace(EXAMPLE_SIGNATURE_RE, 'by: <example-signature>');
|
|
327
|
-
if (PERSONA_RE.test(subject)) hits.push(`${rel}:${i + 1} ${lines[i].trim().slice(0, 120)}`);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
assert(hits.length === 0,
|
|
331
|
-
`身份同意规则仍写死具体 agent 名(应写「当前会话的助手本人」):\n${hits.join('\n')}`);
|
|
332
|
-
|
|
333
|
-
// Removing the wording must not pass the scan: the rule still has to say
|
|
334
|
-
// whose consent counts, and that the user's own request never does.
|
|
335
|
-
const rules = files.find((file) => file.rel === 'src/router/routing-rules.json').text;
|
|
336
|
-
assert(rules.includes('当前会话的助手本人(consent.holder=assistant)'),
|
|
337
|
-
'whoMayConsent 未声明同意权归「当前会话的助手本人」');
|
|
338
|
-
assert(rules.includes('用户单方面要求永远不构成同意'), '「用户单方面要求不算同意」的语义丢了');
|
|
339
|
-
const catalogue = files.find((file) => file.rel === 'docs/ERRORS.md').text;
|
|
340
|
-
assert(catalogue.includes('本规则与被判断的 agent 具体叫什么无关'),
|
|
341
|
-
'ERRORS.md 缺少「本规则与 agent 具体叫什么无关」的说明');
|
|
342
|
-
});
|
|
343
|
-
}
|