kibi-cli 2.17.0 → 2.19.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/dist/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/cli-register-proof.js +9 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -6
- package/dist/commands/sync/manifest.d.ts +10 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +27 -0
- package/dist/commands/sync/untracked-targets.d.ts +17 -0
- package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
- package/dist/commands/sync/untracked-targets.js +81 -9
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +76 -6
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +56 -33
- package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applicability.js +5 -2
- package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-catalog-4.js +4 -0
- package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-loader.js +3 -0
- package/dist/operations/modeling/predicate-score-3.js +1 -1
- package/dist/operations/modeling/predicate-types.d.ts +5 -0
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.d.ts +9 -0
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +30 -1
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +10 -1
- package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -1
- package/dist/prolog/store-lock.d.ts +41 -0
- package/dist/prolog/store-lock.d.ts.map +1 -1
- package/dist/prolog/store-lock.js +91 -1
- package/dist/prolog.d.ts +7 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +21 -3
- package/dist/proof/inspect.d.ts +8 -1
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/inspect.js +19 -2
- package/dist/public/operations/catalog.d.ts +50 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +4 -4
- package/dist/public/operations/discovery-executors.d.ts +50 -1
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +418 -8
- package/dist/public/operations/migration-plan.d.ts +2 -0
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +31 -0
- package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
- package/dist/public/operations/source-relationship-parity.js +55 -1
- package/dist/public/operations/specs/discovery.d.ts +50 -1
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +56 -1
- package/dist/public/skills/kibi-usage/SKILL.md +20 -3
- package/dist/public/skills/kibi-usage/resources/proof.md +23 -0
- package/dist/runtime/cli-runtime.d.ts.map +1 -1
- package/dist/runtime/cli-runtime.js +7 -2
- package/dist/search-answer.d.ts +12 -3
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +51 -4
- package/dist/utils/branch-store.d.ts +8 -0
- package/dist/utils/branch-store.d.ts.map +1 -1
- package/dist/utils/branch-store.js +27 -1
- package/dist/utils/content-freshness.d.ts +13 -0
- package/dist/utils/content-freshness.d.ts.map +1 -0
- package/dist/utils/content-freshness.js +171 -0
- package/dist/utils/symbol-manifest-layout.d.ts +9 -0
- package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
- package/dist/utils/symbol-manifest-layout.js +18 -0
- package/package.json +2 -2
- package/src/public/operations/contracts.ts +7 -4
- package/src/public/operations/discovery-executors.ts +556 -6
- package/src/public/operations/migration-plan.ts +44 -0
- package/src/public/operations/source-relationship-parity.ts +60 -3
- package/src/public/operations/specs/discovery.ts +67 -1
- package/src/public/skills/kibi-usage/SKILL.md +20 -3
- package/src/public/skills/kibi-usage/resources/proof.md +23 -0
|
@@ -7,11 +7,71 @@ import { SwiplResolutionError } from "../../prolog/swipl-resolver.js";
|
|
|
7
7
|
import { buildSearchAnswer } from "../../search-answer.js";
|
|
8
8
|
import { rankEntities } from "../../search-ranking.js";
|
|
9
9
|
import { resolveBranchAttachment } from "../../utils/branch-resolver.js";
|
|
10
|
-
import { BRANCH_STORE_NOT_COMPILED, branchStoreReason, inspectBranchStore, storeLockJournalReason, uncompiledBranchStoreReason, } from "../../utils/branch-store.js";
|
|
10
|
+
import { BRANCH_STORE_NOT_COMPILED, branchStoreReason, inspectBranchStore, storeLockJournalReason, storeLockTakeoverReason, uncompiledBranchStoreReason, } from "../../utils/branch-store.js";
|
|
11
|
+
import { confirmSourceNewerReasons } from "../../utils/content-freshness.js";
|
|
11
12
|
import { loadSearchCandidates, paginateResults, queryEntitiesViaQuery, reloadFullEntities, validateEntityType, } from "./discovery-entities.js";
|
|
12
13
|
import { buildActionsFromStatus, readMigrationConfigStatus, } from "./migration-plan.js";
|
|
13
|
-
import { OperationJsonDecodeError, runOperationJsonQuery, } from "./prolog-json.js";
|
|
14
|
+
import { OperationJsonDecodeError, runOperationJsonQuery, toPrologAtom, } from "./prolog-json.js";
|
|
14
15
|
import { readWorkspaceSnapshot } from "./workspace-snapshot.js";
|
|
16
|
+
export const FACT_KINDS = [
|
|
17
|
+
"subject",
|
|
18
|
+
"property_value",
|
|
19
|
+
"predicate",
|
|
20
|
+
"predicate_schema",
|
|
21
|
+
"observation",
|
|
22
|
+
"meta",
|
|
23
|
+
"rule",
|
|
24
|
+
"rule_schema",
|
|
25
|
+
];
|
|
26
|
+
/** Which typed filters each question mode reads; any other is refused. */
|
|
27
|
+
const MODE_FILTERS = {
|
|
28
|
+
schemas: new Set(["factKind", "predicate", "namespace"]),
|
|
29
|
+
predicate: new Set([
|
|
30
|
+
"predicate",
|
|
31
|
+
"namespace",
|
|
32
|
+
"where",
|
|
33
|
+
"args",
|
|
34
|
+
"polarity",
|
|
35
|
+
"scope",
|
|
36
|
+
"distinct",
|
|
37
|
+
]),
|
|
38
|
+
property: new Set(["subjectKey", "propertyKey", "scope"]),
|
|
39
|
+
fact_kind: new Set(["factKind"]),
|
|
40
|
+
};
|
|
41
|
+
const TYPED_FACT_FILTERS = [
|
|
42
|
+
"factKind",
|
|
43
|
+
"predicate",
|
|
44
|
+
"namespace",
|
|
45
|
+
"where",
|
|
46
|
+
"args",
|
|
47
|
+
"polarity",
|
|
48
|
+
"scope",
|
|
49
|
+
"distinct",
|
|
50
|
+
"subjectKey",
|
|
51
|
+
"propertyKey",
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* An empty result from a store that was never compiled says nothing about
|
|
55
|
+
* the authored requirements, so search reports the store state itself (the
|
|
56
|
+
* same reason kb_status and kb_check use) instead of a bare "no results".
|
|
57
|
+
*/
|
|
58
|
+
// implements REQ-cli-status-pre-first-sync, REQ-mcp-search-discovery
|
|
59
|
+
function uncompiledSearchNotice(context) {
|
|
60
|
+
const attachment = context.branchAttachment;
|
|
61
|
+
if (attachment === undefined)
|
|
62
|
+
return null;
|
|
63
|
+
const store = inspectBranchStore(context.workspaceRoot, attachment.kbBranch);
|
|
64
|
+
const reason = uncompiledBranchStoreReason(context.workspaceRoot, store, attachment.kbBranch);
|
|
65
|
+
if (reason === null)
|
|
66
|
+
return null;
|
|
67
|
+
return {
|
|
68
|
+
state: "not_compiled",
|
|
69
|
+
code: BRANCH_STORE_NOT_COMPILED,
|
|
70
|
+
path: store.path,
|
|
71
|
+
detail: String(reason.detail),
|
|
72
|
+
remediation: reason.remediation,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
15
75
|
function requireProlog(context) {
|
|
16
76
|
if (!context.prolog) {
|
|
17
77
|
throw new Error("Discovery operation requires a Prolog context");
|
|
@@ -28,11 +88,334 @@ function isStatusPayload(value) {
|
|
|
28
88
|
typeof record.dirty === "boolean" &&
|
|
29
89
|
typeof record.syncState === "string");
|
|
30
90
|
}
|
|
91
|
+
function hasTypedFactFilters(input) {
|
|
92
|
+
return TYPED_FACT_FILTERS.some((key) => input[key] !== undefined);
|
|
93
|
+
}
|
|
94
|
+
function requireAtomString(value, field) {
|
|
95
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
96
|
+
throw new Error(`${field} must be a non-empty string.`);
|
|
97
|
+
}
|
|
98
|
+
return value.trim();
|
|
99
|
+
}
|
|
100
|
+
function requireEnum(value, field, allowed) {
|
|
101
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
102
|
+
throw new Error(`${field} must be one of: ${allowed.join(", ")}.`);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Builds the Prolog argument spec of a predicate question. Named `where`
|
|
108
|
+
* entries are resolved against the predicate's declared argument names in
|
|
109
|
+
* Prolog; positional `args` use `null` as a wildcard.
|
|
110
|
+
*/
|
|
111
|
+
// implements REQ-kibi-query-fact-filters
|
|
112
|
+
function predicateArgSpec(input) {
|
|
113
|
+
if (input.where !== undefined && input.args !== undefined) {
|
|
114
|
+
throw new Error("Use either where (named arguments) or args (positional), not both.");
|
|
115
|
+
}
|
|
116
|
+
if (input.where !== undefined) {
|
|
117
|
+
if (input.where === null ||
|
|
118
|
+
typeof input.where !== "object" ||
|
|
119
|
+
Array.isArray(input.where)) {
|
|
120
|
+
throw new Error("where must be an object of argument name to value.");
|
|
121
|
+
}
|
|
122
|
+
const pairs = Object.entries(input.where).map(([key, value]) => {
|
|
123
|
+
const name = requireAtomString(key, "where argument name");
|
|
124
|
+
const atom = requireAtomString(value, `where.${key}`);
|
|
125
|
+
return `${toPrologAtom(name)}=${toPrologAtom(atom)}`;
|
|
126
|
+
});
|
|
127
|
+
return `where([${pairs.join(",")}])`;
|
|
128
|
+
}
|
|
129
|
+
if (input.args !== undefined) {
|
|
130
|
+
if (!Array.isArray(input.args)) {
|
|
131
|
+
throw new Error("args must be an array of constants (null = any).");
|
|
132
|
+
}
|
|
133
|
+
const entries = input.args.map((value, index) => value === null || value === "*"
|
|
134
|
+
? "'$any'"
|
|
135
|
+
: toPrologAtom(requireAtomString(value, `args[${index}]`)));
|
|
136
|
+
return `[${entries.join(",")}]`;
|
|
137
|
+
}
|
|
138
|
+
return "[]";
|
|
139
|
+
}
|
|
140
|
+
function callText(args) {
|
|
141
|
+
return `kb_query(${JSON.stringify(args)})`;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Every typed answer names the exact call that makes sense next, so an
|
|
145
|
+
* agent never has to guess the follow-up from the shape of the data.
|
|
146
|
+
*/
|
|
147
|
+
// implements REQ-kibi-query-fact-filters
|
|
148
|
+
function factQueryNext(input, response) {
|
|
149
|
+
const next = [];
|
|
150
|
+
const first = response.rows[0];
|
|
151
|
+
const firstId = typeof first?.id === "string" ? first.id : undefined;
|
|
152
|
+
switch (response.mode) {
|
|
153
|
+
case "predicate": {
|
|
154
|
+
if (firstId !== undefined) {
|
|
155
|
+
next.push({
|
|
156
|
+
call: callText({ id: firstId }),
|
|
157
|
+
why: "full fact text and relationships of one row",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const filters = {
|
|
161
|
+
predicate: input.predicate,
|
|
162
|
+
...(input.namespace !== undefined
|
|
163
|
+
? { namespace: input.namespace }
|
|
164
|
+
: {}),
|
|
165
|
+
...(input.where !== undefined ? { where: input.where } : {}),
|
|
166
|
+
...(input.args !== undefined ? { args: input.args } : {}),
|
|
167
|
+
...(input.polarity !== undefined ? { polarity: input.polarity } : {}),
|
|
168
|
+
...(input.scope !== undefined ? { scope: input.scope } : {}),
|
|
169
|
+
};
|
|
170
|
+
const names = Array.isArray(response.schema?.argumentNames)
|
|
171
|
+
? response.schema.argumentNames.filter((name) => typeof name === "string")
|
|
172
|
+
: [];
|
|
173
|
+
const bound = new Set(Object.keys(input.where ?? {}));
|
|
174
|
+
const free = names.filter((name) => !bound.has(name));
|
|
175
|
+
if (input.distinct === undefined &&
|
|
176
|
+
response.count > 0 &&
|
|
177
|
+
free.length > 0) {
|
|
178
|
+
const argument = free[free.length - 1] ?? free[0];
|
|
179
|
+
next.push({
|
|
180
|
+
call: callText({ ...filters, distinct: argument }),
|
|
181
|
+
why: `count the distinct values of '${argument}' across these facts`,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if (response.count === 0 &&
|
|
185
|
+
response.suggestions &&
|
|
186
|
+
typeof response.suggestions === "object" &&
|
|
187
|
+
(response.warnings ?? []).length === 0) {
|
|
188
|
+
// Suggest a correction only for an argument whose requested value
|
|
189
|
+
// does not exist; a key whose value exists is not the cause.
|
|
190
|
+
for (const [key, values] of Object.entries(response.suggestions)) {
|
|
191
|
+
if (!Array.isArray(values))
|
|
192
|
+
continue;
|
|
193
|
+
const requested = input.where?.[key];
|
|
194
|
+
if (requested !== undefined && values.includes(requested))
|
|
195
|
+
continue;
|
|
196
|
+
const value = values[0];
|
|
197
|
+
if (typeof value === "string") {
|
|
198
|
+
next.push({
|
|
199
|
+
call: callText({
|
|
200
|
+
...filters,
|
|
201
|
+
where: { ...(input.where ?? {}), [key]: value },
|
|
202
|
+
}),
|
|
203
|
+
why: `'${key}' has no fact with that value; '${value}' is one that exists`,
|
|
204
|
+
});
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (next.length === 0) {
|
|
210
|
+
next.push({
|
|
211
|
+
call: callText({ factKind: "predicate_schema" }),
|
|
212
|
+
why: "list the predicates this KB can answer about and their argument names",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
case "property": {
|
|
218
|
+
if (firstId !== undefined) {
|
|
219
|
+
next.push({
|
|
220
|
+
call: callText({ id: firstId }),
|
|
221
|
+
why: "full fact text and relationships of one row",
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (input.propertyKey !== undefined) {
|
|
225
|
+
next.push({
|
|
226
|
+
call: callText({ subjectKey: input.subjectKey }),
|
|
227
|
+
why: "every property stated about this subject",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
else if (response.count === 0) {
|
|
231
|
+
next.push({
|
|
232
|
+
call: callText({ factKind: "subject" }),
|
|
233
|
+
why: "list the subject keys that exist",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case "schemas": {
|
|
239
|
+
const name = typeof first?.name === "string" ? first.name : undefined;
|
|
240
|
+
if (name !== undefined) {
|
|
241
|
+
next.push({
|
|
242
|
+
call: callText({ predicate: name }),
|
|
243
|
+
why: "every stored fact of the first listed predicate",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
case "fact_kind": {
|
|
249
|
+
if (input.factKind === "predicate") {
|
|
250
|
+
next.push({
|
|
251
|
+
call: callText({ factKind: "predicate_schema" }),
|
|
252
|
+
why: "argument names and constants for asking by name",
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
else if (input.factKind === "property_value" && first) {
|
|
256
|
+
const subjectKey = typeof first.subject_key === "string" ? first.subject_key : undefined;
|
|
257
|
+
if (subjectKey !== undefined) {
|
|
258
|
+
next.push({
|
|
259
|
+
call: callText({ subjectKey }),
|
|
260
|
+
why: "the properties of the first listed subject",
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else if (firstId !== undefined) {
|
|
265
|
+
next.push({
|
|
266
|
+
call: callText({ id: firstId }),
|
|
267
|
+
why: "full fact text of one row",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return next;
|
|
274
|
+
}
|
|
275
|
+
function factRowSummary(row) {
|
|
276
|
+
const id = String(row.id ?? "");
|
|
277
|
+
if (typeof row.name === "string" && Array.isArray(row.args)) {
|
|
278
|
+
const effect = typeof row.effect === "string" ? ` -> ${row.effect}` : "";
|
|
279
|
+
const polarity = row.polarity === "deny" ? " [deny]" : "";
|
|
280
|
+
return `${id}: ${row.name}(${row.args.map(String).join(", ")})${effect}${polarity}`;
|
|
281
|
+
}
|
|
282
|
+
if (typeof row.subjectKey === "string" &&
|
|
283
|
+
typeof row.propertyKey === "string") {
|
|
284
|
+
const unit = typeof row.unit === "string" && row.unit ? ` ${row.unit}` : "";
|
|
285
|
+
return `${id}: ${row.subjectKey} ${row.propertyKey} ${String(row.operator ?? "")} ${String(row.value ?? "")}${unit}`;
|
|
286
|
+
}
|
|
287
|
+
if (typeof row.name === "string" && typeof row.arity === "number") {
|
|
288
|
+
const names = Array.isArray(row.argumentNames)
|
|
289
|
+
? row.argumentNames.map(String).join(", ")
|
|
290
|
+
: "";
|
|
291
|
+
return `${row.name}(${names})`;
|
|
292
|
+
}
|
|
293
|
+
return `${id} (${String(row.title ?? "")})`;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Typed fact questions run as validated Prolog goals in the discovery module;
|
|
297
|
+
* the executor never forwards caller-written Prolog.
|
|
298
|
+
*/
|
|
299
|
+
// implements REQ-kibi-query-fact-filters
|
|
300
|
+
async function executeFactQuery(input, context, limit, offset) {
|
|
301
|
+
if (input.type !== undefined && input.type !== "fact") {
|
|
302
|
+
throw new Error("Typed fact filters (factKind, predicate, subjectKey, ...) apply to type 'fact' only.");
|
|
303
|
+
}
|
|
304
|
+
for (const key of ["id", "tags", "sourceFile"]) {
|
|
305
|
+
if (input[key] !== undefined) {
|
|
306
|
+
throw new Error(`${key} cannot be combined with typed fact filters; run a separate kb_query.`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const prolog = requireProlog(context);
|
|
310
|
+
if (!Number.isFinite(limit) ||
|
|
311
|
+
!Number.isFinite(offset) ||
|
|
312
|
+
limit < 0 ||
|
|
313
|
+
offset < 0) {
|
|
314
|
+
throw new Error("limit and offset must be non-negative numbers.");
|
|
315
|
+
}
|
|
316
|
+
const page = `${Math.trunc(limit)}, ${Math.trunc(offset)}`;
|
|
317
|
+
const namespace = input.namespace === undefined
|
|
318
|
+
? "none"
|
|
319
|
+
: toPrologAtom(requireAtomString(input.namespace, "namespace"));
|
|
320
|
+
const mode = input.factKind === "predicate_schema"
|
|
321
|
+
? "schemas"
|
|
322
|
+
: input.predicate !== undefined
|
|
323
|
+
? "predicate"
|
|
324
|
+
: input.subjectKey !== undefined
|
|
325
|
+
? "property"
|
|
326
|
+
: input.factKind !== undefined
|
|
327
|
+
? "fact_kind"
|
|
328
|
+
: undefined;
|
|
329
|
+
if (mode === undefined) {
|
|
330
|
+
throw new Error("Typed fact filters need a predicate, a subjectKey or a factKind; where/args/polarity/distinct/propertyKey refine one of those.");
|
|
331
|
+
}
|
|
332
|
+
const unused = TYPED_FACT_FILTERS.filter((key) => input[key] !== undefined && !MODE_FILTERS[mode].has(key));
|
|
333
|
+
if (unused.length > 0) {
|
|
334
|
+
throw new Error(`${unused.join(", ")} do${unused.length === 1 ? "es" : ""} not apply to a ${mode} question; allowed with it: ${[...MODE_FILTERS[mode]].join(", ")}.`);
|
|
335
|
+
}
|
|
336
|
+
const scope = input.scope === undefined
|
|
337
|
+
? "all"
|
|
338
|
+
: requireEnum(input.scope, "scope", ["all", "current_requirements"]);
|
|
339
|
+
const factKind = input.factKind === undefined
|
|
340
|
+
? undefined
|
|
341
|
+
: requireEnum(input.factKind, "factKind", FACT_KINDS);
|
|
342
|
+
let goal;
|
|
343
|
+
if (mode === "schemas") {
|
|
344
|
+
goal = `discovery:ask_schemas_json(${namespace}, ${input.predicate === undefined ? "none" : toPrologAtom(requireAtomString(input.predicate, "predicate"))}, ${page}, JsonString)`;
|
|
345
|
+
}
|
|
346
|
+
else if (mode === "predicate") {
|
|
347
|
+
const name = requireAtomString(input.predicate, "predicate");
|
|
348
|
+
const polarity = input.polarity === undefined
|
|
349
|
+
? "none"
|
|
350
|
+
: toPrologAtom(requireEnum(input.polarity, "polarity", ["assert", "deny"]));
|
|
351
|
+
const distinct = input.distinct === undefined
|
|
352
|
+
? "none"
|
|
353
|
+
: toPrologAtom(requireAtomString(input.distinct, "distinct"));
|
|
354
|
+
goal = `discovery:ask_predicate_json(${namespace}, ${toPrologAtom(name)}, ${predicateArgSpec(input)}, ${polarity}, ${scope}, ${distinct}, ${page}, JsonString)`;
|
|
355
|
+
}
|
|
356
|
+
else if (mode === "property") {
|
|
357
|
+
const subject = requireAtomString(input.subjectKey, "subjectKey");
|
|
358
|
+
const property = input.propertyKey === undefined
|
|
359
|
+
? "none"
|
|
360
|
+
: toPrologAtom(requireAtomString(input.propertyKey, "propertyKey"));
|
|
361
|
+
goal = `discovery:ask_property_json(${toPrologAtom(subject)}, ${property}, ${scope}, ${page}, JsonString)`;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
goal = `discovery:ask_fact_kind_json(${toPrologAtom(factKind)}, ${page}, JsonString)`;
|
|
365
|
+
}
|
|
366
|
+
const response = await runOperationJsonQuery(prolog, "discovery.pl", goal, "Query execution", context.signal);
|
|
367
|
+
const rows = Array.isArray(response.rows) ? response.rows : [];
|
|
368
|
+
const count = typeof response.count === "number" ? response.count : rows.length;
|
|
369
|
+
const warnings = Array.isArray(response.warnings) ? response.warnings : [];
|
|
370
|
+
const factQuery = {
|
|
371
|
+
mode: response.mode,
|
|
372
|
+
...(response.predicate ? { predicate: response.predicate } : {}),
|
|
373
|
+
...(response.property ? { property: response.property } : {}),
|
|
374
|
+
...(response.factKind ? { factKind: response.factKind } : {}),
|
|
375
|
+
...(response.schema !== undefined ? { schema: response.schema } : {}),
|
|
376
|
+
...(response.subjectFacts ? { subjectFacts: response.subjectFacts } : {}),
|
|
377
|
+
...(response.distinct !== undefined ? { distinct: response.distinct } : {}),
|
|
378
|
+
...(response.suggestions !== undefined
|
|
379
|
+
? { suggestions: response.suggestions }
|
|
380
|
+
: {}),
|
|
381
|
+
warnings,
|
|
382
|
+
next: factQueryNext(input, { ...response, rows, count }),
|
|
383
|
+
};
|
|
384
|
+
const subject = response.mode === "predicate"
|
|
385
|
+
? `${String(input.predicate)} facts`
|
|
386
|
+
: response.mode === "property"
|
|
387
|
+
? `properties of ${String(input.subjectKey)}`
|
|
388
|
+
: response.mode === "schemas"
|
|
389
|
+
? "predicate schemas"
|
|
390
|
+
: `${String(factKind)} facts`;
|
|
391
|
+
const lines = [
|
|
392
|
+
count === 0
|
|
393
|
+
? `No ${subject} matched.`
|
|
394
|
+
: `${count} ${subject}. Showing ${rows.length} (offset ${offset}, limit ${limit}): ${rows.map(factRowSummary).join("; ")}`,
|
|
395
|
+
...(factQuery.distinct && typeof factQuery.distinct === "object"
|
|
396
|
+
? [
|
|
397
|
+
`Distinct ${String(factQuery.distinct.argument)}: ${JSON.stringify(factQuery.distinct.values ?? [])}`,
|
|
398
|
+
]
|
|
399
|
+
: []),
|
|
400
|
+
...(factQuery.suggestions && typeof factQuery.suggestions === "object"
|
|
401
|
+
? [`Values that exist: ${JSON.stringify(factQuery.suggestions)}`]
|
|
402
|
+
: []),
|
|
403
|
+
...warnings.map((warning) => `Warning: ${warning}`),
|
|
404
|
+
...factQuery.next.map((entry) => `Next: ${entry.call} (${entry.why})`),
|
|
405
|
+
];
|
|
406
|
+
return {
|
|
407
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
408
|
+
structuredContent: { entities: rows, count, factQuery },
|
|
409
|
+
};
|
|
410
|
+
}
|
|
31
411
|
export async function executeQuery(input, context) {
|
|
32
412
|
// implements REQ-kibi-operation-interface-parity
|
|
33
413
|
const { type, id, tags, sourceFile, limit = 100, offset = 0 } = input;
|
|
34
414
|
try {
|
|
35
415
|
validateEntityType(type);
|
|
416
|
+
if (hasTypedFactFilters(input)) {
|
|
417
|
+
return await executeFactQuery(input, context, limit, offset);
|
|
418
|
+
}
|
|
36
419
|
const prolog = requireProlog(context);
|
|
37
420
|
const signal = context.signal;
|
|
38
421
|
const pageInput = {
|
|
@@ -118,6 +501,15 @@ export async function executeSearch(input, context) {
|
|
|
118
501
|
validateEntityType(type);
|
|
119
502
|
try {
|
|
120
503
|
const prolog = requireProlog(context);
|
|
504
|
+
const branch = context.branchAttachment?.kbBranch ?? null;
|
|
505
|
+
const storeNotice = uncompiledSearchNotice(context);
|
|
506
|
+
const branchFields = {
|
|
507
|
+
branch,
|
|
508
|
+
...(storeNotice ? { branchStore: storeNotice } : {}),
|
|
509
|
+
};
|
|
510
|
+
const storeNote = storeNotice
|
|
511
|
+
? ` Note: ${storeNotice.detail} Run kibi sync before relying on search results.`
|
|
512
|
+
: "";
|
|
121
513
|
const intentMode = rankingMode === "intent-v1" ||
|
|
122
514
|
semanticFacets !== undefined ||
|
|
123
515
|
sourceLocations !== undefined;
|
|
@@ -142,12 +534,12 @@ export async function executeSearch(input, context) {
|
|
|
142
534
|
: undefined;
|
|
143
535
|
// The structured payload carries every row; the text is a short
|
|
144
536
|
// human-readable digest instead of a second copy of the results.
|
|
145
|
-
const text = intentResult.matches.length === 0
|
|
537
|
+
const text = `${intentResult.matches.length === 0
|
|
146
538
|
? `No intent search results for '${trimmedQuery}' (abstained).`
|
|
147
539
|
: `Found ${intentResult.matches.length} intent search results for '${trimmedQuery}'${intentResult.analysis.ambiguous ? " (top matches are close; confirm before relying on one)" : ""}. Top: ${paginated
|
|
148
540
|
.slice(0, 5)
|
|
149
541
|
.map((match) => String(match.entity.id ?? ""))
|
|
150
|
-
.join(", ")}${answerLayer ? `. Governing: ${answerLayer.governing.map((req) => req.id).join(", ") || "none found"}` : ""}
|
|
542
|
+
.join(", ")}${answerLayer ? `. Governing: ${answerLayer.governing.map((req) => req.id).join(", ") || "none found"}` : ""}.`}${storeNote}`;
|
|
151
543
|
return {
|
|
152
544
|
content: [{ type: "text", text }],
|
|
153
545
|
structuredContent: {
|
|
@@ -156,6 +548,7 @@ export async function executeSearch(input, context) {
|
|
|
156
548
|
truncated: offset + paginated.length < intentResult.matches.length,
|
|
157
549
|
queryAnalysis: intentResult.analysis,
|
|
158
550
|
...(answerLayer ? { answer: answerLayer } : {}),
|
|
551
|
+
...branchFields,
|
|
159
552
|
},
|
|
160
553
|
};
|
|
161
554
|
}
|
|
@@ -168,17 +561,18 @@ export async function executeSearch(input, context) {
|
|
|
168
561
|
}, context.signal);
|
|
169
562
|
const matches = await rankEntities(entities, trimmedQuery, context.workspaceRoot);
|
|
170
563
|
const paginated = paginateResults(matches, limit, offset);
|
|
171
|
-
const text = matches.length === 0
|
|
564
|
+
const text = `${matches.length === 0
|
|
172
565
|
? `No search results for '${trimmedQuery}'.`
|
|
173
566
|
: `Found ${matches.length} search results for '${trimmedQuery}'. Showing ${paginated.length} (offset ${offset}, limit ${limit}): ${paginated
|
|
174
567
|
.map((match) => `${String(match.entity.id ?? "")} [${match.reasons.join(", ")}]`)
|
|
175
|
-
.join(", ")}`;
|
|
568
|
+
.join(", ")}`}${storeNote}`;
|
|
176
569
|
return {
|
|
177
570
|
content: [{ type: "text", text }],
|
|
178
571
|
structuredContent: {
|
|
179
572
|
results: await projectMatches(prolog, paginated, fields),
|
|
180
573
|
count: matches.length,
|
|
181
574
|
truncated: offset + paginated.length < matches.length,
|
|
575
|
+
...branchFields,
|
|
182
576
|
},
|
|
183
577
|
};
|
|
184
578
|
}
|
|
@@ -272,7 +666,19 @@ export async function executeStatus(_input, context) {
|
|
|
272
666
|
await ownedEngine?.terminate();
|
|
273
667
|
}
|
|
274
668
|
const snapshotEvidence = await readWorkspaceSnapshot(context);
|
|
275
|
-
|
|
669
|
+
// implements REQ-cli-status-pre-first-sync
|
|
670
|
+
// Prolog compares file times only. A fresh clone or a checkout rewrites
|
|
671
|
+
// every mtime without changing a byte, so confirm each "newer" source
|
|
672
|
+
// against the content hashes the last compilation recorded.
|
|
673
|
+
const prologReasons = payload.staleReasons ?? [];
|
|
674
|
+
const confirmed = confirmSourceNewerReasons(context.workspaceRoot, store.path, prologReasons);
|
|
675
|
+
const existingReasons = confirmed.kept;
|
|
676
|
+
if (confirmed.dropped.length > 0 &&
|
|
677
|
+
confirmed.kept.length === 0 &&
|
|
678
|
+
payload.staleReasonsTruncated !== true &&
|
|
679
|
+
payload.syncState === "stale") {
|
|
680
|
+
payload = { ...payload, syncState: "fresh", dirty: false };
|
|
681
|
+
}
|
|
276
682
|
// implements REQ-cli-status-pre-first-sync
|
|
277
683
|
// A missing or empty store beside authored sources gets the one reason
|
|
278
684
|
// whose remediation works (kibi sync); `kibi branch ensure` alone would
|
|
@@ -280,6 +686,9 @@ export async function executeStatus(_input, context) {
|
|
|
280
686
|
const storeReason = uncompiledBranchStoreReason(context.workspaceRoot, store, attachment.kbBranch) ?? branchStoreReason(store);
|
|
281
687
|
const storeUncompiled = storeReason?.code === BRANCH_STORE_NOT_COMPILED;
|
|
282
688
|
const lockReason = storeLockJournalReason(store.path);
|
|
689
|
+
// implements REQ-core-journaled-engine-persistence
|
|
690
|
+
// A stale-lock takeover heals itself; report it without making the KB stale.
|
|
691
|
+
const takeoverNotice = storeLockTakeoverReason(store.path);
|
|
283
692
|
const compilerReason = compilerFingerprintReason(store.path);
|
|
284
693
|
const engineReason = engineStatus
|
|
285
694
|
? {
|
|
@@ -318,6 +727,7 @@ export async function executeStatus(_input, context) {
|
|
|
318
727
|
...(storeUncompiled ? { syncState: "stale", dirty: true } : {}),
|
|
319
728
|
branchAttachment: attachment,
|
|
320
729
|
branchStore: store,
|
|
730
|
+
...(takeoverNotice ? { notices: [takeoverNotice] } : {}),
|
|
321
731
|
...(engineStatus ? { engineStatus } : {}),
|
|
322
732
|
staleReasons,
|
|
323
733
|
staleReasonCount: staleReasons.length,
|
|
@@ -447,7 +857,7 @@ export async function executeStatus(_input, context) {
|
|
|
447
857
|
content: [
|
|
448
858
|
{
|
|
449
859
|
type: "text",
|
|
450
|
-
text: `Branch ${payload.branch} is ${enrichedPayload.syncState} (snapshot ${payload.snapshotId}, dirty=${enrichedPayload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})${storeUncompiled ? `. Blocking: ${String(storeReason?.detail)} Run kibi sync.` : ""}`,
|
|
860
|
+
text: `Branch ${payload.branch} is ${enrichedPayload.syncState} (snapshot ${payload.snapshotId}, dirty=${enrichedPayload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})${storeUncompiled ? `. Blocking: ${String(storeReason?.detail)} Run kibi sync.` : ""}${takeoverNotice ? `. Note: ${String(takeoverNotice.detail)}` : ""}`,
|
|
451
861
|
},
|
|
452
862
|
],
|
|
453
863
|
structuredContent: statusWithPlan,
|
|
@@ -94,6 +94,8 @@ export type MigrationConfigStatus = Readonly<{
|
|
|
94
94
|
configHash: string | null;
|
|
95
95
|
}>;
|
|
96
96
|
export declare function readMigrationConfigStatus(workspaceRoot: string): MigrationConfigStatus;
|
|
97
|
+
/** Migration action code of the automatic recompile of changed sources. */
|
|
98
|
+
export declare const FRESHNESS_RESYNC = "freshness_resync";
|
|
97
99
|
export declare function buildActionsFromStatus(input: {
|
|
98
100
|
workspaceRoot: string;
|
|
99
101
|
branchAttachment?: Readonly<Record<string, unknown>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration-plan.d.ts","sourceRoot":"","sources":["../../../src/public/operations/migration-plan.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migration-plan.d.ts","sourceRoot":"","sources":["../../../src/public/operations/migration-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { BRANCH_STORE_NOT_COMPILED } from "../../utils/branch-store.js";
|
|
5
5
|
import { legacyConfigExists } from "../../utils/config.js";
|
|
6
|
+
import { MTIME_SOURCE_REASON_CODES } from "../../utils/content-freshness.js";
|
|
6
7
|
import { readKbManifestStatus } from "../../utils/kb-manifest.js";
|
|
7
8
|
import { getSchemaVersionStatus, normalizeSchemaVersion, } from "../../utils/schema-version.js";
|
|
8
9
|
import { lifecycleActionsFromViolations, schema6ReviewActionsFromCheck, } from "./schema6-check-actions.js";
|
|
@@ -438,6 +439,8 @@ function readLegacySchemaVersion(workspaceRoot) {
|
|
|
438
439
|
}
|
|
439
440
|
}
|
|
440
441
|
// implements REQ-agent-guided-migration-orchestration
|
|
442
|
+
/** Migration action code of the automatic recompile of changed sources. */
|
|
443
|
+
export const FRESHNESS_RESYNC = "freshness_resync";
|
|
441
444
|
export function buildActionsFromStatus(input) {
|
|
442
445
|
const actions = [];
|
|
443
446
|
const attachment = input.branchAttachment;
|
|
@@ -562,11 +565,39 @@ export function buildActionsFromStatus(input) {
|
|
|
562
565
|
autoApplicable: true,
|
|
563
566
|
}));
|
|
564
567
|
}
|
|
568
|
+
// implements REQ-cli-status-pre-first-sync
|
|
569
|
+
// Sources whose content changed since the last compilation (kb_status has
|
|
570
|
+
// already dropped mtime-only reasons) are fixed by recompiling them: one
|
|
571
|
+
// automatic `kibi sync`, not a review per file. An uncompiled store is
|
|
572
|
+
// compiled by the step above, which also covers these files.
|
|
573
|
+
const resyncReasons = (input.staleReasons ?? []).filter((reason) => typeof reason.code === "string" &&
|
|
574
|
+
MTIME_SOURCE_REASON_CODES.has(reason.code));
|
|
575
|
+
if (resyncReasons.length > 0 && uncompiled === undefined) {
|
|
576
|
+
actions.push(migrationAction({
|
|
577
|
+
id: "freshness-resync",
|
|
578
|
+
code: FRESHNESS_RESYNC,
|
|
579
|
+
category: "freshness",
|
|
580
|
+
safety: "automatic",
|
|
581
|
+
invocation: { kind: "cli", command_argv: ["kibi", "sync"] },
|
|
582
|
+
affectedFiles: [
|
|
583
|
+
...new Set(resyncReasons.flatMap((reason) => typeof reason.path === "string" ? [reason.path] : [])),
|
|
584
|
+
].sort(),
|
|
585
|
+
evidence: { reasons: resyncReasons },
|
|
586
|
+
dependsOn: actions
|
|
587
|
+
.map((action) => action.id)
|
|
588
|
+
.filter((id) => id === "branch-store-ensure" || id === "schema-config-upgrade"),
|
|
589
|
+
postconditions: [{ syncState: "fresh" }],
|
|
590
|
+
autoApplicable: true,
|
|
591
|
+
}));
|
|
592
|
+
}
|
|
565
593
|
for (const reason of input.staleReasons ?? []) {
|
|
566
594
|
const code = typeof reason.code === "string" ? reason.code : "stale_source";
|
|
567
595
|
// Planned above as an automatic compile step, not a review.
|
|
568
596
|
if (code === BRANCH_STORE_NOT_COMPILED)
|
|
569
597
|
continue;
|
|
598
|
+
// Planned above as one automatic resync (or covered by the compile).
|
|
599
|
+
if (MTIME_SOURCE_REASON_CODES.has(code))
|
|
600
|
+
continue;
|
|
570
601
|
const file = typeof reason.path === "string" ? reason.path : "";
|
|
571
602
|
actions.push(migrationAction({
|
|
572
603
|
id: `freshness-${code}-${file}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-relationship-parity.d.ts","sourceRoot":"","sources":["../../../src/public/operations/source-relationship-parity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"source-relationship-parity.d.ts","sourceRoot":"","sources":["../../../src/public/operations/source-relationship-parity.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACpC,CAAC,CAAC;AAaH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,wBAAwB,EAAE,EAC7C,QAAQ,EAAE,SAAS,wBAAwB,EAAE,GAC5C,SAAS,EAAE,CAwDb;AAuCD,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,wBAAwB,EAAE,CAmB5B;AA6BD,wBAAsB,yCAAyC,CAC7D,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,SAAS,EAAE,CAAC,CA0DtB"}
|