fraim-hub 2.0.341 → 2.0.342
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.
|
@@ -27,7 +27,7 @@ const HEADER_FIELDS = [
|
|
|
27
27
|
'runId', 'status', 'pauseReason', 'createdAt', 'lastUpdatedAt', 'issueNumber',
|
|
28
28
|
'sourceTrigger', 'blocked', 'stopped', 'reviewApproved', 'compareMode', 'compareRunId',
|
|
29
29
|
'managedByRunId', 'managedByPersonaKey', 'managedReviewStatus', 'delegationTaskId',
|
|
30
|
-
'humanCoachingDisabled', 'personaSnapshot', 'reviewHandoff',
|
|
30
|
+
'humanCoachingDisabled', 'personaSnapshot', 'reviewHandoff', 'proposalSet',
|
|
31
31
|
];
|
|
32
32
|
const HEADER_FIELD_SET = new Set([...HEADER_FIELDS, 'personaSnapshot', 'delegation', 'reviewHandoff']);
|
|
33
33
|
/**
|
|
@@ -571,7 +571,7 @@ class AiHubConversationStore {
|
|
|
571
571
|
try {
|
|
572
572
|
const parsed = JSON.parse(fs_1.default.readFileSync(this.indexPath(bucketDir), 'utf8'));
|
|
573
573
|
if (Array.isArray(parsed.headers)) {
|
|
574
|
-
return { activeId: parsed.activeId ?? null, headers: parsed.headers };
|
|
574
|
+
return { version: parsed.version, activeId: parsed.activeId ?? null, headers: parsed.headers };
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
catch {
|
|
@@ -583,12 +583,12 @@ class AiHubConversationStore {
|
|
|
583
583
|
// source of truth) and persist it best-effort.
|
|
584
584
|
loadIndex(bucketDir, bucketKey) {
|
|
585
585
|
const existing = this.readIndex(bucketDir);
|
|
586
|
-
if (existing) {
|
|
586
|
+
if (existing && existing.version === 3) {
|
|
587
587
|
const sanitized = sanitizeBucketIndex(existing);
|
|
588
588
|
if (sanitized.changed) {
|
|
589
589
|
try {
|
|
590
590
|
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), {
|
|
591
|
-
version:
|
|
591
|
+
version: 3,
|
|
592
592
|
bucketKey,
|
|
593
593
|
activeId: sanitized.index.activeId,
|
|
594
594
|
headers: sanitized.index.headers,
|
|
@@ -601,10 +601,13 @@ class AiHubConversationStore {
|
|
|
601
601
|
return sanitized.index;
|
|
602
602
|
}
|
|
603
603
|
const headers = this.readAllConversations(bucketDir, bucketKey).map(toHeader).sort(newestFirst);
|
|
604
|
-
|
|
604
|
+
// Older indexes predate proposalSet in headers. Rebuild from canonical shards
|
|
605
|
+
// once so pending reviews are visible immediately after upgrading.
|
|
606
|
+
const activeId = existing?.activeId && headers.some((header) => header.id === existing.activeId) ? existing.activeId : null;
|
|
607
|
+
const rebuilt = { activeId, headers };
|
|
605
608
|
if (headers.length > 0) {
|
|
606
609
|
try {
|
|
607
|
-
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version:
|
|
610
|
+
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version: 3, bucketKey, activeId, headers });
|
|
608
611
|
}
|
|
609
612
|
catch {
|
|
610
613
|
/* best-effort */
|
|
@@ -623,7 +626,7 @@ class AiHubConversationStore {
|
|
|
623
626
|
writeIndex(bucketDir, bucketKey, activeId, conversations) {
|
|
624
627
|
const headers = conversations.map(toHeader).sort(newestFirst);
|
|
625
628
|
const validActive = activeId && headers.some((h) => h.id === activeId) ? activeId : null;
|
|
626
|
-
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version:
|
|
629
|
+
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version: 3, bucketKey, activeId: validActive, headers });
|
|
627
630
|
}
|
|
628
631
|
// Author a whole bucket to exactly `conversations` (used by migration and replaceProject).
|
|
629
632
|
writeBucket(bucketKey, state) {
|
|
@@ -872,7 +875,7 @@ class AiHubConversationStore {
|
|
|
872
875
|
const state = (0, conversation_store_lock_1.withBucketLock)(this.lockPath(bucketDir), () => {
|
|
873
876
|
const idx = this.loadIndex(bucketDir, key);
|
|
874
877
|
const valid = activeId && idx.headers.some((h) => h.id === activeId) ? activeId : null;
|
|
875
|
-
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version:
|
|
878
|
+
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version: 3, bucketKey: key, activeId: valid, headers: idx.headers });
|
|
876
879
|
return normalizeProjectState(key, { activeId: valid, conversations: idx.headers });
|
|
877
880
|
});
|
|
878
881
|
if (!bucketAlreadyExisted)
|
|
@@ -937,7 +940,7 @@ class AiHubConversationStore {
|
|
|
937
940
|
if (!conversationExists) {
|
|
938
941
|
(0, conversation_search_projection_1.deleteConversationSearchEntry)(bucketDir, conversationId);
|
|
939
942
|
if (headers.length !== index.headers.length || activeId !== index.activeId) {
|
|
940
|
-
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version:
|
|
943
|
+
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version: 3, bucketKey: key, activeId, headers });
|
|
941
944
|
}
|
|
942
945
|
return { removed: false, activeId };
|
|
943
946
|
}
|
|
@@ -947,7 +950,7 @@ class AiHubConversationStore {
|
|
|
947
950
|
(0, conversation_search_projection_1.deleteConversationSearchEntry)(bucketDir, conversationId);
|
|
948
951
|
fs_1.default.rmSync(this.indexPath(bucketDir), { force: true });
|
|
949
952
|
fs_1.default.rmSync(conversationPath, { force: true });
|
|
950
|
-
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version:
|
|
953
|
+
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version: 3, bucketKey: key, activeId, headers });
|
|
951
954
|
return { removed: true, activeId };
|
|
952
955
|
});
|
|
953
956
|
}
|
|
@@ -960,7 +963,7 @@ class AiHubConversationStore {
|
|
|
960
963
|
let nextActive = activeId === undefined ? idx.activeId : activeId;
|
|
961
964
|
if (nextActive && !headers.some((h) => h.id === nextActive))
|
|
962
965
|
nextActive = null;
|
|
963
|
-
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version:
|
|
966
|
+
(0, atomic_json_file_1.writeJsonAtomic)(this.indexPath(bucketDir), { version: 3, bucketKey, activeId: nextActive, headers });
|
|
964
967
|
return normalizeProjectState(bucketKey, { activeId: nextActive, conversations: headers });
|
|
965
968
|
}
|
|
966
969
|
stateFromIndex(bucketDir, bucketKey) {
|
|
@@ -69,7 +69,7 @@ ${buildDeferredToolBootstrapSection(profile)}1. **Confirm FRAIM activation**:
|
|
|
69
69
|
Use this process only when the user explicitly invokes FRAIM, names a FRAIM job, asks what FRAIM job to run, or the active surface has already selected a FRAIM job. For ordinary requests, answer or work normally; do not scan FRAIM stubs first.
|
|
70
70
|
|
|
71
71
|
2. **If the user did not specify a FRAIM job or topic after activation**:
|
|
72
|
-
If local FRAIM job stubs are present in the workspace,
|
|
72
|
+
If local FRAIM job stubs are present in the workspace, read that jobs directory's \`INDEX.md\` first (it lists every job's name, one-line intent, and category) and match the request against it; open an individual stub only to confirm a shortlisted candidate, or fall back to scanning the stubs directly if no \`INDEX.md\` is present. Also check \`fraim/personalized-employee/jobs/\` (and its own \`INDEX.md\`, if present) for local overrides or repo-specific jobs. If local files are missing or you cannot inspect workspace files, call \`list_fraim_jobs()\` to view the full catalog, including any proxy-discoverable personalized jobs.
|
|
73
73
|
|
|
74
74
|
3. **Find the match**:
|
|
75
75
|
If the user names an exact FRAIM job, call \`get_fraim_job({ job: "<job-name>" })\` directly. Otherwise, match the user's request to a FRAIM job from the local stub catalog, \`fraim/personalized-employee/jobs/\`, or the full \`list_fraim_jobs()\` response. If no exact or high-confidence job match exists, call \`get_fraim_job({ job: "adhoc-prompt" })\` directly and execute it with the user's instructions as the task input — no confirmation question, and do not pick the nearest catalog job.
|
|
@@ -520,33 +520,60 @@ class LocalRegistryResolver {
|
|
|
520
520
|
// publish or sync and resolves at all under `single-machine`).
|
|
521
521
|
const managerLocalContent = this.readManagerLocalOverlayFile(path);
|
|
522
522
|
if (managerLocalContent !== null) {
|
|
523
|
+
// Issue #1867: apply inheritance so an extends-based override merges with its parent.
|
|
524
|
+
let resolvedManagerLocal;
|
|
525
|
+
try {
|
|
526
|
+
resolvedManagerLocal = await this.resolveInheritance(managerLocalContent, path);
|
|
527
|
+
}
|
|
528
|
+
catch {
|
|
529
|
+
resolvedManagerLocal = { content: managerLocalContent, imports: [] };
|
|
530
|
+
}
|
|
523
531
|
return {
|
|
524
|
-
content:
|
|
532
|
+
content: resolvedManagerLocal.content,
|
|
525
533
|
source: 'local',
|
|
526
534
|
personalized: true,
|
|
527
|
-
inherited:
|
|
535
|
+
inherited: resolvedManagerLocal.imports.length > 0,
|
|
536
|
+
imports: resolvedManagerLocal.imports.length > 0 ? resolvedManagerLocal.imports : undefined,
|
|
528
537
|
scope: 'manager'
|
|
529
538
|
};
|
|
530
539
|
}
|
|
531
540
|
// Issue #869: manager overlay (precedence 3 — the synced cross-machine copy)
|
|
532
541
|
const managerOverlayContent = this.readManagerOverlayFile(path);
|
|
533
542
|
if (managerOverlayContent !== null) {
|
|
543
|
+
// Issue #1867: apply inheritance so an extends-based override merges with its parent.
|
|
544
|
+
let resolvedManagerOverlay;
|
|
545
|
+
try {
|
|
546
|
+
resolvedManagerOverlay = await this.resolveInheritance(managerOverlayContent, path);
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
resolvedManagerOverlay = { content: managerOverlayContent, imports: [] };
|
|
550
|
+
}
|
|
534
551
|
return {
|
|
535
|
-
content:
|
|
552
|
+
content: resolvedManagerOverlay.content,
|
|
536
553
|
source: 'local',
|
|
537
554
|
personalized: true,
|
|
538
|
-
inherited:
|
|
555
|
+
inherited: resolvedManagerOverlay.imports.length > 0,
|
|
556
|
+
imports: resolvedManagerOverlay.imports.length > 0 ? resolvedManagerOverlay.imports : undefined,
|
|
539
557
|
scope: 'manager'
|
|
540
558
|
};
|
|
541
559
|
}
|
|
542
560
|
// Issue #869 Phase 2: org overlay (precedence 3 — after manager, before synced baseline)
|
|
543
561
|
const orgOverlayContent = this.readOrgOverlayFile(path);
|
|
544
562
|
if (orgOverlayContent !== null) {
|
|
563
|
+
// Issue #1867: apply inheritance so an extends-based override merges with its parent.
|
|
564
|
+
let resolvedOrgOverlay;
|
|
565
|
+
try {
|
|
566
|
+
resolvedOrgOverlay = await this.resolveInheritance(orgOverlayContent, path);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
resolvedOrgOverlay = { content: orgOverlayContent, imports: [] };
|
|
570
|
+
}
|
|
545
571
|
return {
|
|
546
|
-
content:
|
|
572
|
+
content: resolvedOrgOverlay.content,
|
|
547
573
|
source: 'local',
|
|
548
574
|
personalized: true,
|
|
549
|
-
inherited:
|
|
575
|
+
inherited: resolvedOrgOverlay.imports.length > 0,
|
|
576
|
+
imports: resolvedOrgOverlay.imports.length > 0 ? resolvedOrgOverlay.imports : undefined,
|
|
550
577
|
scope: 'org'
|
|
551
578
|
};
|
|
552
579
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-hub",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.342",
|
|
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",
|
|
@@ -231,7 +231,7 @@
|
|
|
231
231
|
"electron-updater": "^6.8.9",
|
|
232
232
|
"express": "^5.2.1",
|
|
233
233
|
"extract-zip": "^2.0.1",
|
|
234
|
-
"fraim": "2.0.
|
|
234
|
+
"fraim": "2.0.342",
|
|
235
235
|
"mongodb": "^7.0.0",
|
|
236
236
|
"node-cron": "4.2.1",
|
|
237
237
|
"node-edge-tts": "^1.2.10",
|
package/public/ai-hub/review.css
CHANGED
|
@@ -362,6 +362,8 @@
|
|
|
362
362
|
.learn-prop .lp-skip.on {
|
|
363
363
|
background: var(--danger, #a24747); color: #fff; border-color: transparent;
|
|
364
364
|
}
|
|
365
|
+
:root[data-theme="dark"] .learn-prop .lp-promote.on,
|
|
366
|
+
:root[data-theme="dark"] .learn-prop .lp-skip.on { color: var(--bg); }
|
|
365
367
|
.learn-prop .lp-reason { font-size: 11.5px; color: var(--muted); line-height: 1.5; }
|
|
366
368
|
.learn-prop .lp-reason b { color: var(--text); font-weight: 600; }
|
|
367
369
|
.learn-prop .lp-why { display: flex; flex-direction: column; gap: 5px; margin-top: 2px; }
|
|
@@ -387,9 +389,8 @@
|
|
|
387
389
|
gets its own slightly darker text rather than restyling every managed-disabled
|
|
388
390
|
control on surfaces this change did not validate. */
|
|
389
391
|
.review-actions.learn-batch .rb-approve.managed-disabled { color: #5f5f5f; }
|
|
390
|
-
/*
|
|
391
|
-
|
|
392
|
-
.learn-prop .lp-level-pick.lp-moot { opacity: 0.55; }
|
|
392
|
+
/* Choosing a level restores a skipped proposal, so these controls stay readable. */
|
|
393
|
+
.learn-prop .lp-level-pick.lp-moot { opacity: 1; }
|
|
393
394
|
:root[data-theme="dark"] .review-actions.learn-batch .rb-approve.managed-disabled { color: #cfcfcf; }
|
|
394
395
|
|
|
395
396
|
/* ── R10: Reverse mentoring items (Got it / Dismiss) ── */
|
package/public/ai-hub/script.js
CHANGED
|
@@ -9366,6 +9366,7 @@ function foldRunIntoConversation(conv, run) {
|
|
|
9366
9366
|
conv.hostSessions = { ...(conv.hostSessions || {}), ...run.hostSessions };
|
|
9367
9367
|
}
|
|
9368
9368
|
const runHandoff = normalizeReviewHandoff(run.reviewHandoff);
|
|
9369
|
+
conv.proposalSet = run.proposalSet || null;
|
|
9369
9370
|
if (runHandoff) {
|
|
9370
9371
|
conv.reviewHandoff = runHandoff;
|
|
9371
9372
|
} else if (run.reviewHandoff) {
|