fraim-hub 2.0.217 → 2.0.219

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.
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AiHubConfiguredAgentStore = void 0;
7
7
  exports.synthesizeDefaultConfiguredAgents = synthesizeDefaultConfiguredAgents;
8
8
  exports.checkConfiguredAgentAvailability = checkConfiguredAgentAvailability;
9
+ exports.checkConfiguredAgentReadiness = checkConfiguredAgentReadiness;
9
10
  exports.projectConfiguredAgent = projectConfiguredAgent;
10
11
  exports.resolveConfiguredAgentForHost = resolveConfiguredAgentForHost;
11
12
  exports.resolveConfiguredAgentEnv = resolveConfiguredAgentEnv;
@@ -78,7 +79,9 @@ function normalizeSetupScript(value) {
78
79
  }
79
80
  function synthesizeDefaultConfiguredAgents(employees) {
80
81
  const timestamp = '1970-01-01T00:00:00.000Z';
81
- return employees.map((employee) => ({
82
+ return employees
83
+ .filter((employee) => employee.available)
84
+ .map((employee) => ({
82
85
  id: `${employee.id}-default`,
83
86
  label: employee.label,
84
87
  description: 'Default local launch for this agent tool.',
@@ -161,6 +164,25 @@ function checkConfiguredAgentAvailability(agent, employees, env = process.env) {
161
164
  }
162
165
  return { id: agent.id, label: agent.label, baseHostId: agent.baseHostId, enabled: agent.enabled, available: reasons.length === 0, reasons, warnings };
163
166
  }
167
+ function checkConfiguredAgentReadiness(agent, employees, env = process.env) {
168
+ const check = checkConfiguredAgentAvailability(agent, employees, env);
169
+ if (!check.available || !agent.setupScript)
170
+ return check;
171
+ try {
172
+ resolveConfiguredAgentEnv(agent);
173
+ return check;
174
+ }
175
+ catch (error) {
176
+ return {
177
+ ...check,
178
+ available: false,
179
+ reasons: [
180
+ ...check.reasons,
181
+ error instanceof Error ? error.message : 'Configured agent setup script failed.',
182
+ ],
183
+ };
184
+ }
185
+ }
164
186
  function projectConfiguredAgent(agent, employees) {
165
187
  const check = checkConfiguredAgentAvailability(agent, employees);
166
188
  return {
@@ -244,7 +266,7 @@ function runSetupScript(setupScript) {
244
266
  env: captureEnv,
245
267
  });
246
268
  if (result.error || result.status !== 0) {
247
- const detail = result.error?.message || result.stderr?.trim() || `exit ${result.status}`;
269
+ const detail = result.error?.message || `exit ${result.status}`;
248
270
  throw new Error(`Configured agent setup script failed: ${detail}`);
249
271
  }
250
272
  return filterSafeEnv(parseEnvLines(result.stdout || ''));
@@ -15,10 +15,11 @@ const conversation_store_lock_1 = require("./conversation-store-lock");
15
15
  // They are NOT filesystem paths and must never be passed through path.resolve.
16
16
  exports.MANAGER_SCOPE_KEY = '@manager';
17
17
  exports.COMPANY_SCOPE_KEY = '@company';
18
- // Heavy fields stripped from a header (the light shape the list UI renders). Everything else
18
+ // Fields stripped from a header (the light shape the list UI renders). Everything else
19
19
  // (id, title, jobId, jobTitle, agentName, personaKey, status, runId, createdAt, lastUpdatedAt,
20
- // scope, reviewHandoff, ...) is kept.
21
- const HEAVY_FIELDS = ['messages', 'events', 'artifacts', 'run', 'delegation'];
20
+ // scope, reviewHandoff, ...) is kept. Client-only runtime flags are omitted too so stale index
21
+ // cache state cannot make the frontend skip lazy body hydration.
22
+ const HEADER_OMITTED_FIELDS = ['messages', 'events', 'artifacts', 'run', 'delegation', '_bodyLoaded', '_stopping'];
22
23
  /**
23
24
  * Issue #708: resolve the conversation store bucket key for a given scope.
24
25
  * - 'manager'/'company' → a stable sentinel key (project-independent home).
@@ -107,6 +108,44 @@ function conversationRichness(conv) {
107
108
  const events = Array.isArray(value?.events) ? value.events.length : 0;
108
109
  return messages + events;
109
110
  }
111
+ function normalizedOptionalString(value) {
112
+ return typeof value === 'string' ? value.trim() : '';
113
+ }
114
+ function sameOptionalField(left, right) {
115
+ const leftValue = normalizedOptionalString(left);
116
+ const rightValue = normalizedOptionalString(right);
117
+ return !leftValue || !rightValue || leftValue === rightValue;
118
+ }
119
+ function sameOptionalProjectPath(left, right) {
120
+ const leftValue = normalizedOptionalString(left);
121
+ const rightValue = normalizedOptionalString(right);
122
+ if (!leftValue || !rightValue)
123
+ return true;
124
+ return canonicalProjectPathKey(leftValue) === canonicalProjectPathKey(rightValue);
125
+ }
126
+ function conversationContinuityCompatible(left, right) {
127
+ const a = left;
128
+ const b = right;
129
+ if (!a || !b)
130
+ return true;
131
+ const existingSwitches = Array.isArray(a.agentSwitches) ? a.agentSwitches.length : 0;
132
+ const incomingSwitches = Array.isArray(b.agentSwitches) ? b.agentSwitches.length : 0;
133
+ if (incomingSwitches > existingSwitches)
134
+ return true;
135
+ if (!sameOptionalField(a.scope, b.scope))
136
+ return false;
137
+ if (!sameOptionalProjectPath(a.projectPath, b.projectPath))
138
+ return false;
139
+ if (!sameOptionalField(a.jobId, b.jobId))
140
+ return false;
141
+ if (!sameOptionalField(a.issueNumber, b.issueNumber))
142
+ return false;
143
+ if (!sameOptionalField(a.configuredAgentId, b.configuredAgentId))
144
+ return false;
145
+ if (!sameOptionalField(a.baseHostId, b.baseHostId))
146
+ return false;
147
+ return true;
148
+ }
110
149
  // Place a raw conversation into a bucket, deduping by id. When the id already exists, keep the
111
150
  // richer copy (more run history); tie-break on newer lastUpdatedAt. Order-independent.
112
151
  function placeConversationInBucket(bucket, conv) {
@@ -122,6 +161,9 @@ function placeConversationInBucket(bucket, conv) {
122
161
  return;
123
162
  }
124
163
  const existing = bucket.conversations[idx];
164
+ if (!conversationContinuityCompatible(existing, conv)) {
165
+ return;
166
+ }
125
167
  const existingScore = conversationRichness(existing);
126
168
  const incomingScore = conversationRichness(conv);
127
169
  if (incomingScore > existingScore) {
@@ -223,6 +265,8 @@ function normalizeConversation(projectPath, raw) {
223
265
  jobId: value.jobId,
224
266
  agentName: value.agentName,
225
267
  status: value.status,
268
+ // Issue #904: carry pauseReason explicitly so it survives the ...value spread round-trip.
269
+ ...(value.pauseReason !== undefined && { pauseReason: value.pauseReason }),
226
270
  createdAt: value.createdAt ?? new Date().toISOString(),
227
271
  lastUpdatedAt: value.lastUpdatedAt ?? value.createdAt ?? new Date().toISOString(),
228
272
  };
@@ -246,7 +290,7 @@ function newestFirst(a, b) {
246
290
  }
247
291
  function toHeader(conv) {
248
292
  const header = { ...conv };
249
- for (const field of HEAVY_FIELDS)
293
+ for (const field of HEADER_OMITTED_FIELDS)
250
294
  delete header[field];
251
295
  return header;
252
296
  }