fraim 2.0.360 → 2.0.361-next.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.
@@ -3,8 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.spawnViaCmdExe = spawnViaCmdExe;
4
4
  exports.buildCmdExeArgs = buildCmdExeArgs;
5
5
  const child_process_1 = require("child_process");
6
- function quoteCmdToken(s) {
7
- return /[\s"&|<>^]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
6
+ function escapeCmd(value) { return value.replace(/([()\][%!^"`<>&|;, *?])/g, '^$1'); }
7
+ /** Preserve native argv across both parsing passes of a CMD %* forwarding shim. */
8
+ function forwardingCmdArgument(value, passes = 2) {
9
+ if (/^[a-zA-Z0-9_./:=+\-]+$/.test(value))
10
+ return value;
11
+ let quoted = '"';
12
+ let slashes = 0;
13
+ for (const character of value) {
14
+ if (character === '\\') {
15
+ slashes++;
16
+ continue;
17
+ }
18
+ quoted += '\\'.repeat(character === '"' ? slashes * 2 + 1 : slashes) + character;
19
+ slashes = 0;
20
+ }
21
+ quoted += '\\'.repeat(slashes * 2) + '"';
22
+ return passes === 1 ? escapeCmd(quoted) : escapeCmd(escapeCmd(quoted));
8
23
  }
9
24
  /**
10
25
  * Spawn a command through cmd.exe on Windows so .cmd shims resolve correctly.
@@ -12,12 +27,8 @@ function quoteCmdToken(s) {
12
27
  * containing spaces are not double-escaped by Node's CreateProcess call.
13
28
  */
14
29
  function spawnViaCmdExe(command, args, options) {
15
- const comSpec = process.env.ComSpec || 'cmd.exe';
16
- const tokens = [command, ...args].map(quoteCmdToken);
17
- const innerCmd = tokens.join(' ');
18
- const needsOuterQuote = /[\s"&|<>^]/.test(command);
19
- const cmdString = needsOuterQuote ? `"${innerCmd}"` : innerCmd;
20
- return (0, child_process_1.spawnSync)(comSpec, ['/d', '/s', '/c', cmdString], {
30
+ const invocation = buildCmdExeArgs(command, args);
31
+ return (0, child_process_1.spawnSync)(invocation.command, invocation.args, {
21
32
  ...options,
22
33
  windowsHide: true,
23
34
  windowsVerbatimArguments: true,
@@ -28,12 +39,9 @@ function spawnViaCmdExe(command, args, options) {
28
39
  * Returns the command, args, and a verbatim flag to set windowsVerbatimArguments: true.
29
40
  */
30
41
  function buildCmdExeArgs(command, args) {
31
- const tokens = [command, ...args].map(quoteCmdToken);
32
- const innerCmd = tokens.join(' ');
33
- const needsOuterQuote = /[\s"&|<>^]/.test(command);
34
42
  return {
35
43
  command: process.env.ComSpec || 'cmd.exe',
36
- args: ['/d', '/s', '/c', needsOuterQuote ? `"${innerCmd}"` : innerCmd],
44
+ args: ['/d', '/v:off', '/s', '/c', `"${[escapeCmd(command), ...args.map(arg => forwardingCmdArgument(arg, /\.(exe|com)$/i.test(command) ? 1 : 2))].join(' ')}"`],
37
45
  verbatim: true,
38
46
  };
39
47
  }
@@ -143,6 +143,7 @@ class FirstRunServer {
143
143
  this.registerRoutes();
144
144
  }
145
145
  async start(port) {
146
+ this.sessionService.resumePendingOperations();
146
147
  await new Promise((resolve, reject) => {
147
148
  this.httpServer = this.app.listen(port, '127.0.0.1');
148
149
  this.httpServer.once('listening', () => resolve());
@@ -153,6 +154,7 @@ class FirstRunServer {
153
154
  await this.finishPromise;
154
155
  }
155
156
  async stop() {
157
+ this.sessionService.cancelPendingOperations();
156
158
  if (!this.httpServer) {
157
159
  return;
158
160
  }
@@ -274,13 +276,13 @@ class FirstRunServer {
274
276
  return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not trigger login.' });
275
277
  }
276
278
  });
277
- this.app.post('/api/first-run/check-agent', (req, res) => {
279
+ this.app.post('/api/first-run/check-agent', async (req, res) => {
278
280
  try {
279
281
  const { agentId } = req.body || {};
280
282
  if (!agentId || typeof agentId !== 'string') {
281
283
  return res.status(400).json({ error: 'agentId is required.' });
282
284
  }
283
- return res.json(this.sessionService.checkAgentReady(agentId));
285
+ return res.json(await this.sessionService.checkAgentReady(agentId));
284
286
  }
285
287
  catch (error) {
286
288
  return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not check agent.' });
@@ -36,10 +36,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.FIRST_RUN_ROW_IDS = exports.FirstRunSessionService = void 0;
39
+ exports.FIRST_RUN_ROW_IDS = exports.FirstRunSessionService = exports.runProcess = void 0;
40
40
  exports.commandVersion = commandVersion;
41
- exports.runProcess = runProcess;
42
41
  exports.buildFirstRunHubLaunchArgs = buildFirstRunHubLaunchArgs;
42
+ const agent_command_probe_1 = require("../cli/utils/agent-command-probe");
43
43
  const fs_1 = __importDefault(require("fs"));
44
44
  const os_1 = __importDefault(require("os"));
45
45
  const path_1 = __importDefault(require("path"));
@@ -68,24 +68,8 @@ function getFakeStateMode() {
68
68
  return 'default';
69
69
  }
70
70
  function commandVersion(command, extraBinDirs, basePath) {
71
- const executable = process.platform === 'win32' ? 'cmd.exe' : command;
72
- const args = process.platform === 'win32'
73
- ? ['/d', '/s', '/c', `${command} --version`]
74
- : ['--version'];
75
- const pathValue = extraBinDirs && extraBinDirs.length > 0
76
- ? (0, managed_agent_paths_1.appendBinDirsToPath)(basePath ?? process.env.PATH ?? process.env.Path, extraBinDirs)
77
- : basePath;
78
- const recoveredPath = (0, managed_agent_paths_1.buildRecoveredAgentPath)(pathValue ?? process.env.PATH ?? process.env.Path);
79
- const env = { ...process.env, PATH: recoveredPath, Path: recoveredPath };
80
- const result = (0, child_process_1.spawnSync)(executable, args, {
81
- windowsHide: true,
82
- encoding: 'utf8',
83
- timeout: 5000,
84
- env,
85
- });
86
- if (result.status !== 0 || result.error)
87
- return null;
88
- return (0, managed_agent_paths_1.versionFromProbeOutput)(result.stdout, result.stderr);
71
+ const supplied = extraBinDirs?.length ? (0, managed_agent_paths_1.appendBinDirsToPath)(basePath ?? process.env.PATH, extraBinDirs) : basePath;
72
+ return (0, agent_command_probe_1.selectAgentCommandSync)(command, { env: (0, agent_command_probe_1.agentProbeEnvironment)(supplied) })?.version ?? null;
89
73
  }
90
74
  function ensureOutputDirs() {
91
75
  fs_1.default.mkdirSync((0, script_sync_utils_1.getUserFraimDir)(), { recursive: true });
@@ -121,37 +105,7 @@ function appendInstallLog(line) {
121
105
  ensureOutputDirs();
122
106
  fs_1.default.appendFileSync(getInstallLogPath(), `[${new Date().toISOString()}] ${line}${os_1.default.EOL}`);
123
107
  }
124
- function runProcess(command, args, env) {
125
- return new Promise((resolve, reject) => {
126
- const [realCmd, realArgs] = process.platform === 'win32'
127
- ? ['cmd.exe', ['/d', '/s', '/c', command, ...args]]
128
- : [command, args];
129
- const childEnv = { ...process.env };
130
- for (const [key, value] of Object.entries(env || {})) {
131
- if (value === undefined)
132
- delete childEnv[key];
133
- else
134
- childEnv[key] = value;
135
- }
136
- const child = (0, child_process_1.spawn)(realCmd, realArgs, {
137
- env: childEnv,
138
- windowsHide: true,
139
- shell: false,
140
- });
141
- let stdout = '';
142
- let stderr = '';
143
- child.stdout.on('data', (chunk) => { stdout += chunk.toString(); });
144
- child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
145
- child.on('error', reject);
146
- child.on('close', (code) => {
147
- if (code === 0) {
148
- resolve({ stdout, stderr });
149
- return;
150
- }
151
- reject(new Error(`${command} ${args.join(' ')} failed with code ${code}\n${stdout}${stderr}`.trim()));
152
- });
153
- });
154
- }
108
+ exports.runProcess = managed_agent_install_1.runAgentInstallCommand;
155
109
  function findAgentOption(agentId) {
156
110
  return types_1.FIRST_RUN_AGENT_OPTIONS.find((option) => option.id === agentId);
157
111
  }
@@ -210,7 +164,11 @@ function buildFirstRunHubLaunchArgs() {
210
164
  return ['-y', 'fraim-hub@latest'];
211
165
  }
212
166
  class FirstRunSessionService {
167
+ resumePendingOperations() { if (this.operationAbort.signal.aborted)
168
+ this.operationAbort = new AbortController(); }
169
+ cancelPendingOperations() { this.operationAbort.abort(); }
213
170
  constructor(options) {
171
+ this.operationAbort = new AbortController();
214
172
  // Issue #646: the key may be absent when first-run is launched by the macOS
215
173
  // installer (.dmg) instead of `fraim first-run --key=…`. In that case the
216
174
  // wizard prompts the user to paste their key (see setKey / needsKey).
@@ -782,7 +740,8 @@ class FirstRunSessionService {
782
740
  }
783
741
  try {
784
742
  const systemPath = (0, managed_agent_paths_1.stripManagedAgentBinDirsFromPath)(process.env.PATH);
785
- const existingVersion = commandVersion(option.launchCommand, undefined, systemPath);
743
+ const existingVersion = (await (0, agent_command_probe_1.selectAgentCommand)(option.launchCommand, { signal: this.operationAbort.signal }))?.version;
744
+ this.operationAbort.signal.throwIfAborted();
786
745
  if (existingVersion) {
787
746
  this.setAgentInstallStatus(agentId, 'needs-sign-in', `Sign in to ${option.label} to activate it.`);
788
747
  appendInstallLog(`agent-reused ${agentId}`);
@@ -801,10 +760,11 @@ class FirstRunSessionService {
801
760
  // already-managed, or freshly downloaded) and fold its directory into the PATH passed down,
802
761
  // so the standard attempt can actually succeed instead of silently falling through.
803
762
  const npm = await (0, managed_node_runtime_1.ensureManagedNpm)({
763
+ signal: this.operationAbort.signal,
804
764
  onProgress: (message) => appendInstallLog(`managed-node: ${message}`),
805
765
  });
806
766
  const pathWithNpm = (0, managed_agent_paths_1.appendBinDirsToPath)(systemPath, [npm.binDir]);
807
- const outcome = await (0, managed_agent_install_1.installManagedAgent)({ label: option.label, installPackage: option.installPackage, launchCommand: option.launchCommand }, pathWithNpm, { runProcess, commandVersion });
767
+ const outcome = await (0, managed_agent_install_1.installManagedAgent)({ label: option.label, installPackage: option.installPackage, launchCommand: option.launchCommand, npmPath: npm.npmPath, signal: this.operationAbort.signal }, pathWithNpm, { runProcess: exports.runProcess, commandVersion: managed_agent_install_1.verifyInstalledAgentCommand });
808
768
  this.setAgentInstallStatus(agentId, 'needs-sign-in', `Sign in to ${option.label} to activate it.`);
809
769
  appendInstallLog(outcome.outcome === 'standard' ? `agent-installed-standard ${agentId}` : `agent-installed-managed ${agentId}`);
810
770
  this.persist();
@@ -848,7 +808,7 @@ class FirstRunSessionService {
848
808
  };
849
809
  }
850
810
  }
851
- checkAgentReady(agentId) {
811
+ async checkAgentReady(agentId) {
852
812
  const option = findAgentOption(agentId);
853
813
  if (!option) {
854
814
  return { ok: false, ready: false, message: `Unknown agent: ${agentId}` };
@@ -859,7 +819,7 @@ class FirstRunSessionService {
859
819
  this.persist();
860
820
  return { ok: true, ready: true, message: `${option.label} is ready (fake-mode).` };
861
821
  }
862
- const ver = commandVersion(option.launchCommand, (0, managed_agent_paths_1.getManagedAgentBinDirs)());
822
+ const ver = (await (0, agent_command_probe_1.selectAgentCommand)(option.launchCommand, { signal: this.operationAbort.signal }))?.version;
863
823
  if (ver) {
864
824
  this.setAgentInstallStatus(agentId, 'ready', `${option.label} is ready.`);
865
825
  this.updateAgentSummaryRow();
@@ -22,6 +22,7 @@ exports.normalizeLearningDomain = normalizeLearningDomain;
22
22
  exports.applyLearningEntryChange = applyLearningEntryChange;
23
23
  exports.isTruthyFlag = isTruthyFlag;
24
24
  const artifact_paths_1 = require("../../registry/scripts/artifact-paths");
25
+ const pack_home_1 = require("../cli/utils/pack-home");
25
26
  /**
26
27
  * LearningContextBuilder builds the learning context section for a user.
27
28
  *
@@ -39,7 +40,7 @@ const brand_store_1 = require("../core/brand-store");
39
40
  // Issue #1043 round 2: org and manager content lives at one home named by
40
41
  // config. These reads used to hardcode the removed ~/.fraim/{org,manager}
41
42
  // cache, which silently returned nothing once that cache stopped existing.
42
- const pack_home_1 = require("../cli/utils/pack-home");
43
+ const pack_home_2 = require("../cli/utils/pack-home");
43
44
  const learning_domains_1 = require("../config/learning-domains");
44
45
  const entry_authoring_1 = require("../../registry/scripts/learning/entry-authoring");
45
46
  /** Catalog-backed allowlist for existing files, including retired storage identities. */
@@ -87,14 +88,14 @@ Object.defineProperty(exports, "generateLearningEntryId", { enumerable: true, ge
87
88
  * therefore contributes no tier, matching the behavior before round 2.
88
89
  */
89
90
  function managerSecondaryLearningsBase() {
90
- const home = (0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false });
91
+ const home = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
91
92
  if (home.synced)
92
93
  return (0, path_1.join)(home.contentRoot, 'learnings');
93
94
  // pack-home-exempt: deliberate historical secondary location, see above.
94
95
  return (0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'manager', 'learnings');
95
96
  }
96
97
  function getLearningRoots(workspaceRoot) {
97
- const managerHome = (0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false });
98
+ const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
98
99
  const managerHomeBase = (0, path_1.join)(managerHome.contentRoot, 'learnings');
99
100
  const managerCacheBase = managerSecondaryLearningsBase();
100
101
  // Derive the display base from the actual cache path relative to ~/.fraim so
@@ -117,7 +118,7 @@ function sameResolvedPath(a, b) {
117
118
  return (0, path_1.resolve)(a) === (0, path_1.resolve)(b);
118
119
  }
119
120
  function portablePersonalBaseIsLive(globalBase) {
120
- return sameResolvedPath((0, path_1.join)((0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false }).contentRoot, 'learnings'), globalBase)
121
+ return sameResolvedPath((0, path_1.join)((0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false }).contentRoot, 'learnings'), globalBase)
121
122
  || !sameResolvedPath(globalBase, (0, project_fraim_paths_1.getUserFraimLearningsDir)());
122
123
  }
123
124
  function resolvePersonalLearningFile(repoBase, managerCacheBase, managerCacheDisplayBase, globalBase, globalDisplayBase, fileName) {
@@ -389,7 +390,7 @@ function readFrontmatter(content) {
389
390
  /** Configured owning homes only; discovery never provisions or infers a home. */
390
391
  function getLearningInputHomes(workspaceRoot) {
391
392
  const homes = [{ root: workspaceRoot, contentRoot: (0, path_1.dirname)((0, project_fraim_paths_1.getWorkspaceLearningsDir)(workspaceRoot)) },
392
- (0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false }), (0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false })];
393
+ (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false }), (0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false })];
393
394
  const seen = new Set();
394
395
  return homes.filter(home => {
395
396
  const key = (0, learning_source_record_1.normalizeLearningSourcePath)(home.root) + ':' + (0, learning_source_record_1.normalizeLearningSourcePath)(home.contentRoot);
@@ -400,7 +401,7 @@ function getLearningInputHomes(workspaceRoot) {
400
401
  });
401
402
  }
402
403
  /** Guard the physical owner boundary before reading any input bytes. */
403
- function readLearningSourceFile(filePath, homeRoot, contentRoot) {
404
+ function readLearningSourceFile(filePath, homeRoot, contentRoot, configuredPaths) {
404
405
  try {
405
406
  if (!(0, fs_1.lstatSync)(filePath).isFile() || (0, fs_1.lstatSync)(filePath).isSymbolicLink()) {
406
407
  return { status: 'invalid', reason: 'Learning source must be a regular file' };
@@ -410,7 +411,7 @@ function readLearningSourceFile(filePath, homeRoot, contentRoot) {
410
411
  return { status: 'invalid', reason: 'Learning source escapes configured home' };
411
412
  }
412
413
  const content = (0, fs_1.readFileSync)(filePath, 'utf8');
413
- return { ...(0, learning_source_record_1.parseLearningSourceRecord)(content, filePath, homeRoot, contentRoot), content };
414
+ return { ...(0, learning_source_record_1.parseLearningSourceRecord)(content, filePath, homeRoot, contentRoot, configuredPaths), content };
414
415
  }
415
416
  catch {
416
417
  return { status: 'invalid', reason: 'Learning source unavailable' };
@@ -434,7 +435,7 @@ function collectPendingL0SourceFiles(workspaceRoot, resolvedUserId, _roots) {
434
435
  for (const home of homes) {
435
436
  let paths;
436
437
  try {
437
- paths = (0, artifact_paths_1.resolveLearningPaths)(home.root, home.contentRoot);
438
+ paths = (0, pack_home_1.resolvePackArtifactPaths)(home);
438
439
  }
439
440
  catch {
440
441
  continue;
@@ -449,7 +450,7 @@ function collectPendingL0SourceFiles(workspaceRoot, resolvedUserId, _roots) {
449
450
  const file = (0, path_1.join)(directory, name);
450
451
  if (reservations.has((0, learning_source_record_1.normalizeLearningSourcePath)(file)))
451
452
  continue;
452
- const result = readLearningSourceFile(file, home.root, home.contentRoot);
453
+ const result = readLearningSourceFile(file, home.root, home.contentRoot, paths);
453
454
  if (result.status !== 'valid' || result.record.author !== resolvedUserId
454
455
  || result.record.state !== 'pending' || result.record.synthesized
455
456
  || isTruthyFlag(readFrontmatter(result.content).dismissed))
@@ -474,15 +475,17 @@ function collectPendingL0SourceFiles(workspaceRoot, resolvedUserId, _roots) {
474
475
  * it cannot drift from the read path again.
475
476
  */
476
477
  function orgCacheDisplayPath(fileName) {
477
- const cachePath = (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
478
+ const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
478
479
  return userFraimDisplayPathFor(cachePath);
479
480
  }
480
481
  function userFraimDisplayPathFor(absolutePath) {
481
- const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
482
- const rel = absolutePath.startsWith(fraimDir)
483
- ? absolutePath.slice(fraimDir.length).replace(/\\/g, '/').replace(/^\//, '')
484
- : absolutePath.replace(/\\/g, '/');
485
- return (0, project_fraim_paths_1.getUserFraimDisplayPath)(rel);
482
+ const fraimDir = (0, path_1.resolve)((0, project_fraim_paths_1.getUserFraimDirPath)());
483
+ const resolved = (0, path_1.resolve)(absolutePath);
484
+ const rel = (0, path_1.relative)(fraimDir, resolved);
485
+ const isWithin = rel !== '..' && !rel.startsWith('..' + path_1.sep) && !(0, path_1.isAbsolute)(rel);
486
+ return isWithin
487
+ ? (0, project_fraim_paths_1.getUserFraimDisplayPath)(rel.replace(/\\/g, '/'))
488
+ : resolved.replace(/\\/g, '/');
486
489
  }
487
490
  /**
488
491
  * Resolve an L2 org-scope learning file (issue #563): a repo-local override
@@ -497,7 +500,7 @@ function resolveOrgLearningFile(repoLearningsBase, fileName) {
497
500
  if ((0, fs_1.existsSync)(repoPath)) {
498
501
  return { present: true, path: repoPath, displayPath: `${REPO_LEARNINGS_REL}/${fileName}` };
499
502
  }
500
- const cachePath = (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
503
+ const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
501
504
  if ((0, fs_1.existsSync)(cachePath)) {
502
505
  return { present: true, path: cachePath, displayPath: orgCacheDisplayPath(fileName) };
503
506
  }
@@ -510,7 +513,7 @@ function resolveOrgLearningFile(repoLearningsBase, fileName) {
510
513
  */
511
514
  function resolveOrgLearningFileTiers(repoLearningsBase, fileName) {
512
515
  const tiers = [];
513
- const cachePath = (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
516
+ const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
514
517
  if ((0, fs_1.existsSync)(cachePath)) {
515
518
  tiers.push({
516
519
  level: 'org',
@@ -738,7 +741,7 @@ function resolveOrgContextFile(workspaceRoot, relativePath, orgCacheEligible = t
738
741
  };
739
742
  }
740
743
  if (orgCacheEligible) {
741
- const cachePath = (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, relativePath);
744
+ const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, relativePath);
742
745
  if ((0, fs_1.existsSync)(cachePath)) {
743
746
  return {
744
747
  present: true,
@@ -747,20 +750,12 @@ function resolveOrgContextFile(workspaceRoot, relativePath, orgCacheEligible = t
747
750
  }
748
751
  }
749
752
  if (!orgCacheEligible) {
750
- const managerHome = (0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false });
753
+ const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
751
754
  const managerCachePath = (0, path_1.join)(managerHome.contentRoot, relativePath);
752
755
  if ((0, fs_1.existsSync)(managerCachePath)) {
753
- // Derive the display path relative to ~/.fraim. For the standard
754
- // single-machine backend contentRoot is ~/.fraim/personalized-employee
755
- // so the display path is ~/.fraim/personalized-employee/... For a
756
- // synced backend it may be elsewhere (e.g. ~/.fraim/packs/manager-repo).
757
- const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
758
- const relToFraim = managerCachePath.startsWith(fraimDir)
759
- ? managerCachePath.slice(fraimDir.length).replace(/\\/g, '/').replace(/^\//, '')
760
- : `personalized-employee/${relativePath}`;
761
756
  return {
762
757
  present: true,
763
- displayPath: (0, project_fraim_paths_1.getUserFraimDisplayPath)(relToFraim)
758
+ displayPath: userFraimDisplayPathFor(managerCachePath)
764
759
  };
765
760
  }
766
761
  }
@@ -927,7 +922,7 @@ function resolveTeamContextFile(workspaceRoot, key, options = {}) {
927
922
  // destination (R5.1), so writePath stays empty and the caller must route
928
923
  // edits through the org backend's propose-and-approve flow.
929
924
  if (key === 'org' || key === 'orgRules') {
930
- const cachePath = (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false, ...options }).contentRoot, relativePath);
925
+ const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false, ...options }).contentRoot, relativePath);
931
926
  if ((0, fs_1.existsSync)(cachePath)) {
932
927
  const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
933
928
  const orgDisplayPath = cachePath.startsWith(fraimDir)
@@ -944,7 +939,7 @@ function resolveTeamContextFile(workspaceRoot, key, options = {}) {
944
939
  }
945
940
  }
946
941
  if (key === 'manager' || key === 'managerRules') {
947
- const managerHome = (0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false, ...options });
942
+ const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false, ...options });
948
943
  const managerCachePath = (0, path_1.join)(managerHome.contentRoot, relativePath);
949
944
  if ((0, fs_1.existsSync)(managerCachePath)) {
950
945
  const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
@@ -986,7 +981,7 @@ function resolveTeamContextFile(workspaceRoot, key, options = {}) {
986
981
  function orgBrandCandidateDirs(workspaceRoot) {
987
982
  return [
988
983
  (0, path_1.join)((0, project_fraim_paths_1.getWorkspaceFraimDir)(workspaceRoot), 'personalized-employee', 'context'),
989
- (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'context'),
984
+ (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'context'),
990
985
  (0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'personalized-employee', 'context'),
991
986
  ];
992
987
  }
@@ -1561,8 +1556,8 @@ function readPreservedLearnings(workspaceRoot, userId, scope, level = 'machine',
1561
1556
  // Only the human library reads these observations. An unavailable store must
1562
1557
  // fail this read, so a client can retain known history rather than invent zeroes.
1563
1558
  const resolvedUserId = scope === 'org' ? userId : resolveLearningUserId(workspaceRoot, userId, roots);
1564
- const managerHome = (0, pack_home_1.resolvePackHome)('manager', { migrateLegacy: false });
1565
- const orgHome = scope === 'org' ? (0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }) : null;
1559
+ const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
1560
+ const orgHome = scope === 'org' ? (0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }) : null;
1566
1561
  const assessmentHomes = [
1567
1562
  { level: 'Project', root: (0, project_fraim_paths_1.getWorkspaceFraimDir)(workspaceRoot), contentRoot: (0, path_1.join)((0, project_fraim_paths_1.getWorkspaceFraimDir)(workspaceRoot), 'personalized-employee') },
1568
1563
  { level: 'Manager', root: managerHome.root, contentRoot: managerHome.contentRoot },
@@ -1605,8 +1600,8 @@ function readPreservedLearnings(workspaceRoot, userId, scope, level = 'machine',
1605
1600
  // #806: domain-scoped org learnings — union from both locations.
1606
1601
  const allDomainFiles = [
1607
1602
  ...listDomainLearningFiles(roots.repoLearningsBase, 'org', fileType, effectiveCatalog).map((d) => ({ ...d, base: roots.repoLearningsBase })),
1608
- ...listDomainLearningFiles((0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings'), 'org', fileType, effectiveCatalog)
1609
- .map((d) => ({ ...d, base: (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings') })),
1603
+ ...listDomainLearningFiles((0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings'), 'org', fileType, effectiveCatalog)
1604
+ .map((d) => ({ ...d, base: (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings') })),
1610
1605
  ];
1611
1606
  const seenDomainFile = new Set();
1612
1607
  for (const { fileName, domain, base } of allDomainFiles) {
@@ -1667,7 +1662,7 @@ function resolveLearningFilePath(workspaceRoot, userId, ref, effectiveCatalog) {
1667
1662
  // Write org entries to the org pack home — same location share-with-others uses.
1668
1663
  // The Hub read path already merges pack home + repo-local, so edits land where
1669
1664
  // the agent reads them without needing a repo commit.
1670
- return (0, path_1.join)((0, pack_home_1.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', learningFileName('org', fileType, ref.domain, effectiveCatalog));
1665
+ return (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', learningFileName('org', fileType, ref.domain, effectiveCatalog));
1671
1666
  }
1672
1667
  const resolvedUserId = resolveLearningUserId(workspaceRoot, userId, roots);
1673
1668
  // Issue #1070: for the 'machine' level use the configured manager home.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.360",
3
+ "version": "2.0.361-next.0",
4
4
  "description": "FRAIM core CLI and MCP package.",
5
5
  "main": "index.js",
6
6
  "bin": {