@geraldmaron/construct 1.0.23 → 1.1.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/README.md +0 -2
- package/bin/construct +189 -207
- package/lib/agent-instructions/inject.mjs +25 -4
- package/lib/audit-rules.mjs +127 -0
- package/lib/audit-skills.mjs +43 -1
- package/lib/beads-client.mjs +9 -0
- package/lib/beads-optimistic.mjs +23 -71
- package/lib/bridges/copilot-proxy.mjs +116 -0
- package/lib/cli-commands.mjs +5 -1
- package/lib/comment-lint.mjs +1 -1
- package/lib/config/schema.mjs +1 -1
- package/lib/document-extract/docling-client.mjs +16 -6
- package/lib/document-extract/docling-sidecar.py +32 -2
- package/lib/document-extract.mjs +37 -10
- package/lib/document-ingest.mjs +90 -5
- package/lib/embed/inbox.mjs +6 -3
- package/lib/embed/recommendation-store.mjs +7 -289
- package/lib/embed/reconcile.mjs +2 -2
- package/lib/embed/roadmap.mjs +16 -1
- package/lib/engine/consolidate.mjs +160 -3
- package/lib/engine/contradiction-judge.mjs +71 -0
- package/lib/engine/contradiction.mjs +74 -0
- package/lib/hooks/config-protection.mjs +4 -4
- package/lib/hooks/session-reflect.mjs +5 -1
- package/lib/host-capabilities.mjs +30 -0
- package/lib/ingest/docling-remote.mjs +90 -0
- package/lib/ingest/strategy.mjs +1 -1
- package/lib/init-unified.mjs +9 -13
- package/lib/intake/git-queue.mjs +195 -0
- package/lib/intake/queue.mjs +9 -16
- package/lib/logging/rotate.mjs +18 -0
- package/lib/mcp/server.mjs +124 -12
- package/lib/mcp/tool-budget.mjs +53 -0
- package/lib/mcp/tools/storage.mjs +2 -3
- package/lib/mcp-catalog.json +4 -4
- package/lib/mcp-manager.mjs +59 -3
- package/lib/observation-store.mjs +38 -166
- package/lib/ollama/capability-store.mjs +78 -0
- package/lib/ollama/provision-context.mjs +344 -0
- package/lib/opencode-config.mjs +148 -0
- package/lib/opencode-telemetry.mjs +7 -0
- package/lib/orchestration/runtime.mjs +3 -2
- package/lib/orchestration-policy.mjs +41 -6
- package/lib/platforms/capabilities.mjs +100 -0
- package/lib/prompt-composer.js +12 -8
- package/lib/reconcile/agent-instructions-rewrap.mjs +8 -4
- package/lib/reconcile/index.mjs +0 -2
- package/lib/reflect/extractor.mjs +14 -1
- package/lib/reflect/salience.mjs +65 -0
- package/lib/rules-delivery.mjs +122 -0
- package/lib/runtime/uv-bootstrap.mjs +32 -17
- package/lib/service-manager.mjs +79 -259
- package/lib/setup.mjs +44 -425
- package/lib/specialists/prompt-schema.mjs +162 -0
- package/lib/specialists/scaffold.mjs +109 -0
- package/lib/status.mjs +3 -6
- package/lib/storage/admin.mjs +48 -325
- package/lib/storage/backend.mjs +10 -57
- package/lib/storage/embeddings-engine.mjs +19 -5
- package/lib/storage/hybrid-query.mjs +15 -196
- package/lib/storage/sync.mjs +36 -177
- package/lib/storage/vector-client.mjs +256 -235
- package/lib/strategy-store.mjs +35 -286
- package/lib/telemetry/beads-fallback.mjs +40 -0
- package/lib/telemetry/hook-calls.mjs +138 -0
- package/lib/worker/entrypoint.mjs +6 -14
- package/package.json +6 -5
- package/personas/construct.md +1 -1
- package/platforms/capabilities.json +76 -0
- package/platforms/claude/settings.template.json +0 -7
- package/platforms/opencode/sync-config.mjs +121 -25
- package/rules/common/neurodivergent-output.md +1 -1
- package/rules/web/coding-style.md +8 -0
- package/rules/web/design-quality.md +8 -0
- package/rules/web/hooks.md +8 -0
- package/rules/web/patterns.md +8 -0
- package/rules/web/performance.md +8 -0
- package/rules/web/security.md +8 -0
- package/rules/web/testing.md +8 -0
- package/scripts/sync-specialists.mjs +174 -40
- package/specialists/prompts/cx-architect.md +20 -0
- package/specialists/prompts/cx-qa.md +1 -1
- package/specialists/prompts/cx-test-automation.md +12 -0
- package/specialists/registry.json +0 -8
- package/templates/docs/construct_guide.md +1 -1
- package/db/schema/001_init.sql +0 -40
- package/db/schema/002_pgvector.sql +0 -182
- package/db/schema/003_intake.sql +0 -47
- package/db/schema/003_observation_reconciliation.sql +0 -14
- package/db/schema/004_recommendations.sql +0 -46
- package/db/schema/005_strategy.sql +0 -21
- package/db/schema/006_graph.sql +0 -24
- package/db/schema/007_tags.sql +0 -30
- package/db/schema/008_skill_usage.sql +0 -24
- package/db/schema/009_scheduler.sql +0 -14
- package/db/schema/010_cx_scores.sql +0 -51
- package/lib/intake/postgres-queue.mjs +0 -240
- package/lib/reconcile/postgres-namespace.mjs +0 -102
- package/lib/services/local-postgres.mjs +0 -15
- package/lib/storage/backup.mjs +0 -347
- package/lib/storage/migrations.mjs +0 -187
- package/lib/storage/postgres-backup.mjs +0 -124
- package/lib/storage/sql-store.mjs +0 -45
- package/lib/storage/store-version.mjs +0 -115
- package/lib/storage/unified-storage.mjs +0 -550
- package/lib/storage/vector-store.mjs +0 -100
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/specialists/prompt-schema.mjs — validation for hybrid specialist prompt files.
|
|
3
|
+
*
|
|
4
|
+
* Specialist prompts are migrating from opaque free-form markdown to a hybrid
|
|
5
|
+
* shape: YAML frontmatter (structured metadata) + markdown body (the authored
|
|
6
|
+
* prose), mirroring skills/roles/*.md. This validates the frontmatter and the
|
|
7
|
+
* canonical-section contract, and gates the highest-value invariant — the
|
|
8
|
+
* frontmatter `perspective{}` must equal the registry's, since the two
|
|
9
|
+
* currently duplicate each other and drift silently (ADR-0037).
|
|
10
|
+
*
|
|
11
|
+
* A file with no frontmatter is treated as not-yet-converted: reported, never an
|
|
12
|
+
* error, so the linter can run across a half-migrated corpus. Frontmatter is the
|
|
13
|
+
* source of truth; section checks are warnings until the body-normalization
|
|
14
|
+
* phase, so adding frontmatter alone never fails the gate.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import yaml from 'js-yaml';
|
|
20
|
+
|
|
21
|
+
export const REQUIRED_FRONTMATTER = ['name', 'role', 'version', 'perspective'];
|
|
22
|
+
export const PERSPECTIVE_FIELDS = ['bias', 'tension', 'openingQuestion', 'failureMode'];
|
|
23
|
+
export const OPTIONAL_FRONTMATTER = ['roleGuidance', 'roleOverlays', 'templates', 'preloadRoleGuidance'];
|
|
24
|
+
export const REQUIRED_SECTIONS = ['Anti-fabrication contract', 'Output format'];
|
|
25
|
+
export const CANONICAL_SECTIONS = [
|
|
26
|
+
'Orientation', 'Anti-fabrication contract', 'Productive tension',
|
|
27
|
+
'Opening question', 'Failure mode', 'Domain overlays', 'Output format',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Split a prompt file into { frontmatter, body }. frontmatter is null when the
|
|
34
|
+
* file has none (unconverted), or undefined when the YAML failed to parse.
|
|
35
|
+
*/
|
|
36
|
+
export function splitFrontmatter(text) {
|
|
37
|
+
const raw = String(text ?? '');
|
|
38
|
+
const m = raw.match(FRONTMATTER_RE);
|
|
39
|
+
if (!m) return { frontmatter: null, body: raw };
|
|
40
|
+
try {
|
|
41
|
+
const fm = yaml.load(m[1]);
|
|
42
|
+
return { frontmatter: (fm && typeof fm === 'object') ? fm : {}, body: raw.slice(m[0].length) };
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return { frontmatter: undefined, body: raw.slice(m[0].length), error: err.message };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function hasHeading(body, title) {
|
|
49
|
+
const target = String(title).trim().toLowerCase();
|
|
50
|
+
const re = /^#{1,6}\s+(.+)$/gm;
|
|
51
|
+
let m;
|
|
52
|
+
while ((m = re.exec(body))) {
|
|
53
|
+
if (m[1].trim().toLowerCase() === target) return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function samePerspective(a, b) {
|
|
59
|
+
if (!a || !b) return false;
|
|
60
|
+
return PERSPECTIVE_FIELDS.every((f) => String(a[f] ?? '').trim() === String(b[f] ?? '').trim());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate one prompt file's content against the hybrid format.
|
|
65
|
+
*
|
|
66
|
+
* @param {object} opts
|
|
67
|
+
* @param {string} opts.content - raw file content
|
|
68
|
+
* @param {string} [opts.id] - display id (cx-<role>) for messages
|
|
69
|
+
* @param {object} [opts.registryEntry] - the matching registry specialist (for drift checks)
|
|
70
|
+
* @returns {{ converted: boolean, errors: string[], warnings: string[] }}
|
|
71
|
+
*/
|
|
72
|
+
export function validatePromptContent({ content, id = '(prompt)', registryEntry } = {}) {
|
|
73
|
+
const errors = [];
|
|
74
|
+
const warnings = [];
|
|
75
|
+
const { frontmatter, body, error } = splitFrontmatter(content);
|
|
76
|
+
|
|
77
|
+
if (frontmatter === null) {
|
|
78
|
+
return { converted: false, errors, warnings: [`${id}: no frontmatter — not yet converted to the hybrid format`] };
|
|
79
|
+
}
|
|
80
|
+
if (frontmatter === undefined) {
|
|
81
|
+
errors.push(`${id}: frontmatter is not valid YAML — ${error}`);
|
|
82
|
+
return { converted: true, errors, warnings };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const field of REQUIRED_FRONTMATTER) {
|
|
86
|
+
if (frontmatter[field] == null || frontmatter[field] === '') {
|
|
87
|
+
errors.push(`${id}: missing required frontmatter field "${field}"`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (frontmatter.name && registryEntry?.name) {
|
|
92
|
+
const expected = `cx-${registryEntry.name}`;
|
|
93
|
+
if (frontmatter.name !== expected) {
|
|
94
|
+
errors.push(`${id}: frontmatter name "${frontmatter.name}" must equal "${expected}" (registry name)`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (frontmatter.version != null && !Number.isInteger(frontmatter.version)) {
|
|
98
|
+
errors.push(`${id}: frontmatter version must be an integer (got ${JSON.stringify(frontmatter.version)})`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const persp = frontmatter.perspective;
|
|
102
|
+
if (persp && typeof persp === 'object') {
|
|
103
|
+
for (const f of PERSPECTIVE_FIELDS) {
|
|
104
|
+
if (!persp[f] || !String(persp[f]).trim()) errors.push(`${id}: perspective.${f} is required and must be non-empty`);
|
|
105
|
+
}
|
|
106
|
+
if (registryEntry?.perspective && !samePerspective(persp, registryEntry.perspective)) {
|
|
107
|
+
errors.push(`${id}: perspective{} drifts from registry.json — they must match while both exist (frontmatter is the source of truth; update the registry)`);
|
|
108
|
+
}
|
|
109
|
+
} else if (frontmatter.perspective != null) {
|
|
110
|
+
errors.push(`${id}: perspective must be an object with ${PERSPECTIVE_FIELDS.join(', ')}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const known = new Set([...REQUIRED_FRONTMATTER, ...OPTIONAL_FRONTMATTER]);
|
|
114
|
+
for (const key of Object.keys(frontmatter)) {
|
|
115
|
+
if (!known.has(key)) warnings.push(`${id}: unknown frontmatter key "${key}"`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const section of REQUIRED_SECTIONS) {
|
|
119
|
+
if (!hasHeading(body, section)) warnings.push(`${id}: missing canonical section "## ${section}" (warn-only until body normalization)`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { converted: true, errors, warnings };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Validate every specialist prompt file referenced by the registry.
|
|
127
|
+
* @returns {{ errors: string[], warnings: string[], total: number, converted: number }}
|
|
128
|
+
*/
|
|
129
|
+
export function validatePromptFiles({ rootDir = process.cwd(), registry } = {}) {
|
|
130
|
+
const errors = [];
|
|
131
|
+
const warnings = [];
|
|
132
|
+
let total = 0;
|
|
133
|
+
let converted = 0;
|
|
134
|
+
|
|
135
|
+
const reg = registry ?? loadRegistry(rootDir);
|
|
136
|
+
const specialists = Array.isArray(reg?.specialists) ? reg.specialists : [];
|
|
137
|
+
const byName = new Map(specialists.map((s) => [s.name, s]));
|
|
138
|
+
|
|
139
|
+
const seen = new Set();
|
|
140
|
+
for (const entry of specialists) {
|
|
141
|
+
if (!entry?.promptFile || !entry.promptFile.startsWith('specialists/prompts/')) continue;
|
|
142
|
+
if (seen.has(entry.promptFile)) continue;
|
|
143
|
+
seen.add(entry.promptFile);
|
|
144
|
+
const filePath = path.join(rootDir, entry.promptFile);
|
|
145
|
+
if (!fs.existsSync(filePath)) { errors.push(`${entry.name}: promptFile ${entry.promptFile} does not exist`); continue; }
|
|
146
|
+
total += 1;
|
|
147
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
148
|
+
const result = validatePromptContent({ content, id: `cx-${entry.name}`, registryEntry: byName.get(entry.name) });
|
|
149
|
+
if (result.converted) converted += 1;
|
|
150
|
+
errors.push(...result.errors);
|
|
151
|
+
warnings.push(...result.warnings);
|
|
152
|
+
}
|
|
153
|
+
return { errors, warnings, total, converted };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function loadRegistry(rootDir) {
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(fs.readFileSync(path.join(rootDir, 'specialists', 'registry.json'), 'utf8'));
|
|
159
|
+
} catch {
|
|
160
|
+
return { specialists: [] };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/specialists/scaffold.mjs — scaffold and field-edit hybrid specialist prompts.
|
|
3
|
+
*
|
|
4
|
+
* The CLI harness side of ADR-0037: `construct specialist create` emits a
|
|
5
|
+
* canonical skeleton (frontmatter + required sections) a human then fills in,
|
|
6
|
+
* and `construct specialist edit` mutates frontmatter fields only (never the
|
|
7
|
+
* prose body). Both validate against lib/specialists/prompt-schema.mjs before
|
|
8
|
+
* declaring success, so a scaffolded file passes `lint:prompts` immediately.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import yaml from 'js-yaml';
|
|
14
|
+
import { splitFrontmatter, validatePromptContent, PERSPECTIVE_FIELDS } from './prompt-schema.mjs';
|
|
15
|
+
|
|
16
|
+
function promptRelPath(role) {
|
|
17
|
+
return path.join('specialists', 'prompts', `cx-${role}.md`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Render a canonical skeleton for a new specialist. Body sections are stubbed
|
|
22
|
+
* with a one-line placeholder so the file is valid-shaped from creation.
|
|
23
|
+
*/
|
|
24
|
+
export function renderSkeleton({ role, perspective = {}, roleGuidance } = {}) {
|
|
25
|
+
const fm = {
|
|
26
|
+
name: `cx-${role}`,
|
|
27
|
+
role,
|
|
28
|
+
version: 1,
|
|
29
|
+
perspective: {
|
|
30
|
+
bias: perspective.bias || 'TODO: what this role is instinctively suspicious of',
|
|
31
|
+
tension: perspective.tension || 'cx-TODO',
|
|
32
|
+
openingQuestion: perspective.openingQuestion || 'TODO: the first diagnostic question',
|
|
33
|
+
failureMode: perspective.failureMode || 'TODO: how this role fails when absent',
|
|
34
|
+
},
|
|
35
|
+
...(roleGuidance ? { roleGuidance } : {}),
|
|
36
|
+
};
|
|
37
|
+
const body = [
|
|
38
|
+
'## Orientation',
|
|
39
|
+
'',
|
|
40
|
+
'TODO: the role voice — the hard-won instinct that makes this specialist worth consulting.',
|
|
41
|
+
'',
|
|
42
|
+
'## Anti-fabrication contract',
|
|
43
|
+
'',
|
|
44
|
+
'Every load-bearing claim cites a source the reader can re-verify. When a fact is not in the source, write `unknown` or `[unverified]`. See `rules/common/no-fabrication.md`.',
|
|
45
|
+
'',
|
|
46
|
+
'## Output format',
|
|
47
|
+
'',
|
|
48
|
+
'TODO: the artifact this role produces, or the `get_template(...)` it delegates to.',
|
|
49
|
+
'',
|
|
50
|
+
].join('\n');
|
|
51
|
+
return `---\n${yaml.dump(fm).trimEnd()}\n---\n\n${body}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a specialist prompt draft on disk. Refuses to overwrite. Validates
|
|
56
|
+
* the rendered skeleton before writing.
|
|
57
|
+
* @returns {{ path: string, relPath: string }}
|
|
58
|
+
*/
|
|
59
|
+
export function createSpecialistDraft({ rootDir = process.cwd(), role, perspective, roleGuidance } = {}) {
|
|
60
|
+
if (!role || !/^[a-z][a-z0-9-]*$/.test(role)) {
|
|
61
|
+
throw new Error(`invalid role id "${role}" — use lowercase kebab-case (e.g. performance-auditor)`);
|
|
62
|
+
}
|
|
63
|
+
const relPath = promptRelPath(role);
|
|
64
|
+
const filePath = path.join(rootDir, relPath);
|
|
65
|
+
if (fs.existsSync(filePath)) throw new Error(`${relPath} already exists — refusing to overwrite`);
|
|
66
|
+
|
|
67
|
+
const content = renderSkeleton({ role, perspective, roleGuidance });
|
|
68
|
+
const { errors } = validatePromptContent({ content, id: `cx-${role}` });
|
|
69
|
+
if (errors.length) throw new Error(`scaffold failed validation:\n ${errors.join('\n ')}`);
|
|
70
|
+
|
|
71
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
72
|
+
fs.writeFileSync(filePath, content.endsWith('\n') ? content : `${content}\n`);
|
|
73
|
+
return { path: filePath, relPath };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Edit a specialist's frontmatter only (never the prose body). Supported edits:
|
|
78
|
+
* set a perspective field, add a role overlay, or bump the version.
|
|
79
|
+
* @returns {{ path: string, frontmatter: object }}
|
|
80
|
+
*/
|
|
81
|
+
export function editSpecialistFrontmatter({ rootDir = process.cwd(), role, setPerspective = {}, addOverlay, bumpVersion = false } = {}) {
|
|
82
|
+
const relPath = promptRelPath(role);
|
|
83
|
+
const filePath = path.join(rootDir, relPath);
|
|
84
|
+
if (!fs.existsSync(filePath)) throw new Error(`${relPath} does not exist`);
|
|
85
|
+
|
|
86
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
87
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
88
|
+
if (!frontmatter) throw new Error(`${relPath} has no frontmatter — convert it to the hybrid format first`);
|
|
89
|
+
|
|
90
|
+
for (const [field, value] of Object.entries(setPerspective)) {
|
|
91
|
+
if (!PERSPECTIVE_FIELDS.includes(field)) throw new Error(`unknown perspective field "${field}"`);
|
|
92
|
+
frontmatter.perspective = { ...(frontmatter.perspective || {}), [field]: value };
|
|
93
|
+
}
|
|
94
|
+
if (addOverlay) {
|
|
95
|
+
const overlays = new Set(Array.isArray(frontmatter.roleOverlays) ? frontmatter.roleOverlays : []);
|
|
96
|
+
overlays.add(addOverlay);
|
|
97
|
+
frontmatter.roleOverlays = [...overlays];
|
|
98
|
+
}
|
|
99
|
+
if (bumpVersion) {
|
|
100
|
+
frontmatter.version = (Number.isInteger(frontmatter.version) ? frontmatter.version : 0) + 1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const next = `---\n${yaml.dump(frontmatter).trimEnd()}\n---\n${body.startsWith('\n') ? '' : '\n'}${body}`;
|
|
104
|
+
const { errors } = validatePromptContent({ content: next, id: `cx-${role}` });
|
|
105
|
+
if (errors.length) throw new Error(`edit would break validation:\n ${errors.join('\n ')}`);
|
|
106
|
+
|
|
107
|
+
fs.writeFileSync(filePath, next.endsWith('\n') ? next : `${next}\n`);
|
|
108
|
+
return { path: filePath, frontmatter };
|
|
109
|
+
}
|
package/lib/status.mjs
CHANGED
|
@@ -20,9 +20,6 @@ import { resolveExecutionContractModelMetadata, selectModelTierForWorkCategory }
|
|
|
20
20
|
import { loadPluginRegistry } from './plugin-registry.mjs';
|
|
21
21
|
import { readCostLog, summarizeCostData, normalizeCostEntry } from './cost.mjs';
|
|
22
22
|
import { readEfficiencyLog, summarizeEfficiencyData } from './efficiency.mjs';
|
|
23
|
-
import { describeSqlStore, sqlStoreHealth } from './storage/sql-store.mjs';
|
|
24
|
-
import { describeVectorStore } from './storage/vector-store.mjs';
|
|
25
|
-
import { describeSqlStoreHealth } from './storage/sql-store.mjs';
|
|
26
23
|
import { triggerAutoBackfillIfSparse } from './telemetry/backfill.mjs';
|
|
27
24
|
import { resolveTraceBackend, telemetryProviderLabel } from './telemetry/client.mjs';
|
|
28
25
|
const TOTAL_BYTES_WARNING_THRESHOLD = 750_000;
|
|
@@ -709,9 +706,9 @@ export async function buildStatus({
|
|
|
709
706
|
triggerAutoBackfillIfSparse(telemetryRichness, { ...mergedEnv });
|
|
710
707
|
const efficiencyDigest = summarizeEfficiencyData(readEfficiencyLog(homeDir));
|
|
711
708
|
const activeOverlays = getActiveOverlays(cwd);
|
|
712
|
-
const sqlStore =
|
|
713
|
-
const vectorStore =
|
|
714
|
-
const sqlHealth =
|
|
709
|
+
const sqlStore = { mode: 'lancedb', label: 'LanceDB + Git-Backed', dbUrl: null, vectorEnabled: true };
|
|
710
|
+
const vectorStore = { enabled: true, backend: 'lancedb', label: 'Embedded LanceDB' };
|
|
711
|
+
const sqlHealth = { status: 'healthy', message: 'Local embedded' };
|
|
715
712
|
const promotionRequests = getPromotionRequests(cwd);
|
|
716
713
|
const executionContractModel = resolveExecutionContractModelMetadata({
|
|
717
714
|
envValues: mergedEnv,
|