fraim-hub 2.0.299 → 2.0.300

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.
@@ -2164,6 +2164,12 @@ class CliHostRuntime {
2164
2164
  return null;
2165
2165
  return active.pending.length + 1;
2166
2166
  }
2167
+ getPendingQueueLength(hostId, sessionId) {
2168
+ const active = this.activeContinueRuns.get(`${hostId}::${sessionId}`);
2169
+ if (!active)
2170
+ return null;
2171
+ return active.pending.length;
2172
+ }
2167
2173
  stopActiveSession(hostId, sessionId) {
2168
2174
  const key = `${hostId}::${sessionId}`;
2169
2175
  const active = this.activeContinueRuns.get(key);
@@ -9,6 +9,7 @@ exports.resolveHostWorkingDirectory = resolveHostWorkingDirectory;
9
9
  exports.workingDirectoryExists = workingDirectoryExists;
10
10
  const fs_1 = __importDefault(require("fs"));
11
11
  const path_1 = __importDefault(require("path"));
12
+ const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
12
13
  const pack_home_1 = require("../cli/utils/pack-home");
13
14
  const conversation_store_1 = require("./conversation-store");
14
15
  function ensureExistingDirectory(directoryPath, label) {
@@ -26,9 +27,14 @@ function ensureExistingDirectory(directoryPath, label) {
26
27
  return resolved;
27
28
  }
28
29
  function scopeHome(scope) {
29
- return scope === 'manager'
30
- ? (0, pack_home_1.resolvePackHome)('manager').contentRoot
31
- : (0, pack_home_1.resolvePackHome)('org').contentRoot;
30
+ const fallbackRoot = (0, project_fraim_paths_1.getUserFraimDirPath)();
31
+ const packHome = (0, pack_home_1.resolvePackHome)(scope === 'manager' ? 'manager' : 'org');
32
+ const contentRoot = packHome.contentRoot;
33
+ if (fs_1.default.existsSync(contentRoot) && fs_1.default.statSync(contentRoot).isDirectory()) {
34
+ return contentRoot;
35
+ }
36
+ fs_1.default.mkdirSync(fallbackRoot, { recursive: true });
37
+ return fallbackRoot;
32
38
  }
33
39
  function resolveRunWorkingDirectory(scope, candidateProjectPath, defaultProjectPath) {
34
40
  if (scope === 'manager') {
@@ -2138,8 +2138,11 @@ function ensureDirectoryPath(projectPath) {
2138
2138
  }
2139
2139
  return resolved;
2140
2140
  }
2141
- function readRunScope(raw, jobId) {
2142
- if ((raw === undefined || raw === null) && jobId && MACHINE_LEVEL_JOB_IDS.has(jobId)) {
2141
+ function readRunScope(raw, jobId, projectPath) {
2142
+ if ((raw === undefined || raw === null) &&
2143
+ !projectPath?.trim() &&
2144
+ jobId &&
2145
+ MACHINE_LEVEL_JOB_IDS.has(jobId)) {
2143
2146
  return 'manager';
2144
2147
  }
2145
2148
  return raw === 'manager' || raw === 'company' ? raw : 'project';
@@ -6089,7 +6092,7 @@ class AiHubServer {
6089
6092
  try {
6090
6093
  // Issue #892: project-independent (manager/company) runs resolve a working dir
6091
6094
  // even with no project; project runs still require an existing project directory.
6092
- const scope = readRunScope(req.body.scope, req.body.jobId);
6095
+ const scope = readRunScope(req.body.scope, req.body.jobId, req.body.projectPath);
6093
6096
  const projectPath = this.resolveRunProjectPath(req.body.projectPath, scope);
6094
6097
  const requestedHostId = req.body.hostId;
6095
6098
  const instructions = (req.body.instructions || '').trim();
@@ -6590,7 +6593,7 @@ class AiHubServer {
6590
6593
  const body = (req.body ?? {});
6591
6594
  // Issue #892: a mid-flight project-independent onboarding (manager/company) must
6592
6595
  // resume even with no project (e.g. after a Hub restart).
6593
- const scope = readRunScope(body.scope, body.jobId);
6596
+ const scope = readRunScope(body.scope, body.jobId, body.projectPath);
6594
6597
  const projectPath = this.resolveRunProjectPath(body.projectPath, scope);
6595
6598
  const requestedHostId = body.hostId;
6596
6599
  const requestedSessionId = (body.sessionId || '').trim();
@@ -7871,11 +7874,24 @@ class AiHubServer {
7871
7874
  }
7872
7875
  });
7873
7876
  }
7877
+ shouldDeferContinueExitToQueuedWork(runId) {
7878
+ const current = this.runRegistry.get(runId);
7879
+ if (!current?.sessionId)
7880
+ return false;
7881
+ const pending = this.hostRuntime.getPendingQueueLength?.(current.hostId, current.sessionId);
7882
+ return typeof pending === 'number' && pending > 0;
7883
+ }
7874
7884
  // Issue #904: shared exit handler for all FRAIM onExit chokepoints.
7875
7885
  // Classifies the exit (classifyExit), writes pauseReason onto the run,
7876
7886
  // then either parks the run or schedules a recovery continue turn.
7877
7887
  // Must NOT be called from directRun or delegated-child onExit sites.
7878
7888
  handleRunExit(runId, exitCode, postPark) {
7889
+ // CliHostRuntime serializes same-session continue turns internally. In the
7890
+ // real child-process path, the server's onExit listener fires before the
7891
+ // runtime's queue-drain listener. If queued manager messages are waiting,
7892
+ // this close is a handoff point, not the end of the run.
7893
+ if (this.shouldDeferContinueExitToQueuedWork(runId))
7894
+ return;
7879
7895
  // Issue #1360: unconditional safety net -- runs through here regardless
7880
7896
  // of which continueRun call site delivered the turn and regardless of
7881
7897
  // whether the turn emitted any events (see clearStaleDeliveryStatuses).
@@ -55,6 +55,7 @@ exports.HUMAN_MANAGER_PROFILES = {
55
55
  procella: { humanTitle: 'Procurement Manager', keywords: ['"Procurement Manager"', '"Head of Procurement"', '"Sourcing Lead"'] },
56
56
  banke: { humanTitle: 'KYC Operations Manager', keywords: ['"KYC Operations Manager"', '"AML Compliance Manager"', '"Banking Compliance Lead"'] },
57
57
  auditya: { humanTitle: 'Audit Manager', keywords: ['"Audit Manager"', '"Internal Audit Lead"', '"Compliance Audit Manager"'] },
58
+ travis: { humanTitle: 'Travel Operations Manager', keywords: ['"Travel Operations Manager"', '"Corporate Travel Manager"', '"Travel Program Manager"'] },
58
59
  };
59
60
  function getHumanManagerProfile(roleKey) {
60
61
  return exports.HUMAN_MANAGER_PROFILES[roleKey] ?? GENERALIST_PROFILE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-hub",
3
- "version": "2.0.299",
3
+ "version": "2.0.300",
4
4
  "description": "FRAIM Hub local companion package.",
5
5
  "author": "Sid Mathur <sid.mathur@gmail.com>",
6
6
  "homepage": "https://github.com/mathursrus/FRAIM#readme",
@@ -211,7 +211,7 @@
211
211
  "electron-updater": "^6.8.9",
212
212
  "express": "^5.2.1",
213
213
  "extract-zip": "^2.0.1",
214
- "fraim": "2.0.299",
214
+ "fraim": "2.0.300",
215
215
  "mongodb": "^7.0.0",
216
216
  "node-cron": "4.2.1",
217
217
  "node-edge-tts": "^1.2.10",
@@ -8067,10 +8067,10 @@ async function startRun(job, instructions, employeeId, preassignedConvId, invoke
8067
8067
  title: isFreeform ? deriveTitle('', instructions) : deriveTitle(job.title, instructions),
8068
8068
  jobId: job.id,
8069
8069
  jobTitle: job.title,
8070
- agentName: employeeId,
8071
8070
  configuredAgentId: employeeId,
8072
8071
  configuredAgentLabel: configuredAgentLabel(employeeId),
8073
8072
  baseHostId: baseHostIdForAgent(employeeId),
8073
+ agentName: baseHostIdForAgent(employeeId),
8074
8074
  // Structured jobs without a protected employee default to MANdy; freeform
8075
8075
  // described goals remain employee-less until promotion assigns ownership.
8076
8076
  personaKey: assignedPersonaKey,
@@ -8341,8 +8341,9 @@ function foldRunIntoConversation(conv, run) {
8341
8341
  }
8342
8342
  if (run.configuredAgentId) {
8343
8343
  conv.configuredAgentId = run.configuredAgentId;
8344
- conv.agentName = run.configuredAgentId;
8345
- } else if (run.hostId && !conv.configuredAgentId) {
8344
+ }
8345
+ if (run.hostId) {
8346
+ conv.agentName = run.hostId;
8346
8347
  conv.baseHostId = run.hostId;
8347
8348
  }
8348
8349
  if (run.configuredAgentLabel) conv.configuredAgentLabel = run.configuredAgentLabel;
@@ -16720,6 +16721,8 @@ function toggleLockedSharingPanel(backend) {
16720
16721
  var panel = document.getElementById('sharing-panel-' + backend);
16721
16722
  if (!panel) return;
16722
16723
  panel.hidden = !panel.hidden;
16724
+ var warn = document.getElementById('sharing-chooser-warn');
16725
+ if (warn) warn.hidden = !(_sharingLayer === 'manager' && backend === 'git' && !panel.hidden);
16723
16726
  }
16724
16727
 
16725
16728
  function startSharingSetupJob(backend) {