@saasontools/strauss-kb 0.1.5 → 0.1.6
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 +30 -2
- package/dist/{chunk-VOJ6D6OX.js → chunk-BVF7X5VO.js} +2 -2
- package/dist/{chunk-FZIMFPGR.js → chunk-PNSRTKYN.js} +102 -6
- package/dist/chunk-PNSRTKYN.js.map +1 -0
- package/dist/{chunk-KQMGKSPZ.js → chunk-V7TRZ2ER.js} +2 -2
- package/dist/cli-main.cjs +98 -5
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +103 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-main.cjs +98 -5
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-FZIMFPGR.js.map +0 -1
- /package/dist/{chunk-VOJ6D6OX.js.map → chunk-BVF7X5VO.js.map} +0 -0
- /package/dist/{chunk-KQMGKSPZ.js.map → chunk-V7TRZ2ER.js.map} +0 -0
package/README.md
CHANGED
|
@@ -88,6 +88,24 @@ concepts. `type` is the only key OKF requires; `title`, `description`,
|
|
|
88
88
|
OKF's. Unknown keys are preserved rather than stripped, as OKF requires of
|
|
89
89
|
consumers.
|
|
90
90
|
|
|
91
|
+
`verified` is the record's append-only trail of checks. Each entry is OKF's
|
|
92
|
+
actor stamp — `{ by, at }` — and the entries this package's `verify` writes add
|
|
93
|
+
a `note`: what the check actually found, not just that one happened. The `note`
|
|
94
|
+
is a strauss extension key on the entries this tool writes, not an OKF
|
|
95
|
+
requirement on the array, so noteless entries a foreign producer wrote remain
|
|
96
|
+
readable, and prior entries are spread forward untouched rather than reshaped.
|
|
97
|
+
|
|
98
|
+
Who may append is the point. A verifier whose actor equals the record's
|
|
99
|
+
`generated.by` — compared case-insensitively over the whole actor, so case
|
|
100
|
+
drift cannot mint a distinct verifier identity — is refused unless the actor
|
|
101
|
+
is `human:`-prefixed: trust that can be self-granted is not trust, and a
|
|
102
|
+
generator re-reading its own output is not an independent check. The refusal
|
|
103
|
+
is recorded in the log as `verify:refused`, so an audit sees the attempt as
|
|
104
|
+
well as the rule. The `human:` prefix itself is an honor-system label — actor
|
|
105
|
+
identity is self-declared through `STRAUSS_KB_ACTOR`, not an authenticated
|
|
106
|
+
identity claim — which is worth knowing when deciding how much weight a
|
|
107
|
+
human-verified event carries.
|
|
108
|
+
|
|
91
109
|
Anything prefixed `strauss_` is this package's extension, namespaced so a later
|
|
92
110
|
OKF version defining the same name cannot collide:
|
|
93
111
|
|
|
@@ -196,6 +214,7 @@ strauss-kb [--bundle PATH] <command> [args]
|
|
|
196
214
|
status <concept-id> <status> Move a record's status, compare-and-swap.
|
|
197
215
|
supersede <concept-id> <replacement-id> Mark a record superseded, linking both directions.
|
|
198
216
|
answer <concept-id> <answer...> Resolve an open question and append the answer.
|
|
217
|
+
verify <concept-id> --note <text> Append a verified[] event — who checked, when, and what the check found.
|
|
199
218
|
load [type] [--budget N | --all] Hand over the whole base, each record with its standing.
|
|
200
219
|
query <text...> Search; every match arrives flagged with its standing.
|
|
201
220
|
trace <concept-id> [edges...] How a position was arrived at, as a timeline.
|
|
@@ -239,8 +258,8 @@ strauss-kb validate || echo "problems above"
|
|
|
239
258
|
|
|
240
259
|
`strauss-kb-mcp` speaks stdio and takes no API key and no required environment.
|
|
241
260
|
Every CLI verb is a tool: `kb_write`, `kb_write_decision`, `kb_no_decision`,
|
|
242
|
-
`kb_status`, `kb_supersede`, `kb_answer`, `
|
|
243
|
-
`kb_list`, `kb_index`, `kb_log`, `kb_validate`, `kb_schema`, `kb_types`,
|
|
261
|
+
`kb_status`, `kb_supersede`, `kb_answer`, `kb_verify`, `kb_load`, `kb_query`,
|
|
262
|
+
`kb_trace`, `kb_list`, `kb_index`, `kb_log`, `kb_validate`, `kb_schema`, `kb_types`,
|
|
244
263
|
`kb_pin`, `kb_unpin`, `kb_pins`, `kb_context`. Most tools take a `bundlePath`;
|
|
245
264
|
`kb_schema` and `kb_types` describe the format rather than any one base, and
|
|
246
265
|
`kb_pins` and `kb_context` read the workspace pin manifests instead. The one
|
|
@@ -299,6 +318,15 @@ problem:
|
|
|
299
318
|
| Standing | `strauss_status`, the supersession chain | is this still what we hold? |
|
|
300
319
|
| Freshness | `stale_after`, `verified[]` | has anyone confirmed it lately? |
|
|
301
320
|
|
|
321
|
+
Freshness is tiered by who did the confirming. OKF's spec (§5.3) defines the
|
|
322
|
+
trust tiers from the verifying actor's prefix: an empty `verified[]` is
|
|
323
|
+
unverified, an agent-prefixed verifier makes the record machine-confirmed, and
|
|
324
|
+
a `human:`-prefixed verifier makes it human-reviewed. Of that ladder, today's
|
|
325
|
+
adjudication reports only the first rung — the warning it attaches when
|
|
326
|
+
`verified[]` is empty; reporting the full tier is upcoming tooling. When it
|
|
327
|
+
lands, the tier will be derived from the events at read time, never stored, so
|
|
328
|
+
it cannot drift from the trail that justifies it.
|
|
329
|
+
|
|
302
330
|
**Load before you search.** These bases run to a few thousand tokens — twenty
|
|
303
331
|
records measured at about 3,000 — so the first thing to try is taking all of it.
|
|
304
332
|
On nine questions whose wording appears in no record, a reader holding the whole
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
KB_COMMANDS,
|
|
3
3
|
KbStore
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PNSRTKYN.js";
|
|
5
5
|
|
|
6
6
|
// src/mcp.ts
|
|
7
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -45,4 +45,4 @@ export {
|
|
|
45
45
|
createKbMcpServer,
|
|
46
46
|
runKbMcpServer
|
|
47
47
|
};
|
|
48
|
-
//# sourceMappingURL=chunk-
|
|
48
|
+
//# sourceMappingURL=chunk-BVF7X5VO.js.map
|
|
@@ -11,6 +11,11 @@ var kbActorStampSchema = z.object({
|
|
|
11
11
|
by: z.string().min(1),
|
|
12
12
|
at: z.string().min(1)
|
|
13
13
|
}).passthrough();
|
|
14
|
+
var kbVerifiedEventSchema = kbActorStampSchema.extend({
|
|
15
|
+
note: z.string().refine((s) => s.trim().length > 0, {
|
|
16
|
+
message: "note must say what the check found"
|
|
17
|
+
})
|
|
18
|
+
});
|
|
14
19
|
var kbAnchorSchema = z.object({
|
|
15
20
|
file: z.string().min(1),
|
|
16
21
|
symbol: z.string().min(1).optional()
|
|
@@ -1536,8 +1541,37 @@ var validateCommand = define({
|
|
|
1536
1541
|
failsWhen: (result) => Array.isArray(result) && result.length > 0
|
|
1537
1542
|
});
|
|
1538
1543
|
|
|
1539
|
-
// src/commands/
|
|
1544
|
+
// src/commands/verify.ts
|
|
1540
1545
|
import { z as z26 } from "zod";
|
|
1546
|
+
var verifyCommand = define({
|
|
1547
|
+
name: "verify",
|
|
1548
|
+
tool: "kb_verify",
|
|
1549
|
+
usage: "verify <concept-id> --note <text>",
|
|
1550
|
+
description: "Append one verified[] event \u2014 who checked the record, when, and what the check found. Appends only; prior events are never rewritten. A record's own generator is refused unless the actor is human: re-reading your own output is not an independent check.",
|
|
1551
|
+
input: z26.object({
|
|
1552
|
+
bundlePath,
|
|
1553
|
+
conceptId,
|
|
1554
|
+
note: z26.string().refine((s) => s.trim().length > 0, {
|
|
1555
|
+
message: "note must say what the check found"
|
|
1556
|
+
})
|
|
1557
|
+
}),
|
|
1558
|
+
fromArgv: (argv, path) => ({
|
|
1559
|
+
bundlePath: path,
|
|
1560
|
+
conceptId: argv[1],
|
|
1561
|
+
note: argvFlag(argv, "--note")
|
|
1562
|
+
}),
|
|
1563
|
+
run: async ({ store, actor, now }, { bundlePath: path, conceptId: id, note }) => {
|
|
1564
|
+
await assertBaseNotFrozen(process.cwd(), path);
|
|
1565
|
+
const record = await store.verify(path, id, note, actor, now());
|
|
1566
|
+
return {
|
|
1567
|
+
conceptId: record.conceptId,
|
|
1568
|
+
verified: record.frontmatter.verified?.length ?? 0
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
|
|
1573
|
+
// src/commands/write.ts
|
|
1574
|
+
import { z as z27 } from "zod";
|
|
1541
1575
|
var writeCommand = define({
|
|
1542
1576
|
name: "write",
|
|
1543
1577
|
tool: "kb_write",
|
|
@@ -1551,9 +1585,9 @@ var writeCommand = define({
|
|
|
1551
1585
|
"- Prefer a new record over overloading an existing one, and keep each short. A record nobody finishes reading is not durable memory.",
|
|
1552
1586
|
"- Records are never deleted; supersede instead, so the earlier reasoning stays inspectable."
|
|
1553
1587
|
].join("\n"),
|
|
1554
|
-
input:
|
|
1588
|
+
input: z27.object({
|
|
1555
1589
|
bundlePath,
|
|
1556
|
-
type:
|
|
1590
|
+
type: z27.enum(KB_RECORD_TYPES),
|
|
1557
1591
|
input: composeInputSchema
|
|
1558
1592
|
}),
|
|
1559
1593
|
fromArgv: async (argv, path, stdin) => ({
|
|
@@ -1577,7 +1611,7 @@ var writeCommand = define({
|
|
|
1577
1611
|
});
|
|
1578
1612
|
|
|
1579
1613
|
// src/commands/write-decision.ts
|
|
1580
|
-
import { z as
|
|
1614
|
+
import { z as z28 } from "zod";
|
|
1581
1615
|
var writeDecisionCommand = define({
|
|
1582
1616
|
name: "write-decision",
|
|
1583
1617
|
tool: "kb_write_decision",
|
|
@@ -1590,7 +1624,7 @@ var writeDecisionCommand = define({
|
|
|
1590
1624
|
"- `alternative` is what you turned down and why, not a list of everything considered.",
|
|
1591
1625
|
"- A reference to material you read goes in `sources`; a reference to code goes in `anchors`; a reference to another record goes in `relatedConceptIds`."
|
|
1592
1626
|
].join("\n"),
|
|
1593
|
-
input:
|
|
1627
|
+
input: z28.object({ bundlePath, input: decisionInputSchema }),
|
|
1594
1628
|
fromArgv: async (_argv, path, stdin) => ({
|
|
1595
1629
|
bundlePath: path,
|
|
1596
1630
|
input: JSON.parse(await stdin())
|
|
@@ -1618,6 +1652,7 @@ var KB_COMMANDS = [
|
|
|
1618
1652
|
statusCommand,
|
|
1619
1653
|
supersedeCommand,
|
|
1620
1654
|
answerCommand,
|
|
1655
|
+
verifyCommand,
|
|
1621
1656
|
loadCommand,
|
|
1622
1657
|
queryCommand,
|
|
1623
1658
|
traceCommand,
|
|
@@ -1673,6 +1708,7 @@ var ErrorTypes = /* @__PURE__ */ ((ErrorTypes2) => {
|
|
|
1673
1708
|
ErrorTypes2["KbRecordAlreadyExists"] = "KbRecordAlreadyExists";
|
|
1674
1709
|
ErrorTypes2["KbInvalidConceptId"] = "KbInvalidConceptId";
|
|
1675
1710
|
ErrorTypes2["KbRecordNotFound"] = "KbRecordNotFound";
|
|
1711
|
+
ErrorTypes2["KbSelfVerification"] = "KbSelfVerification";
|
|
1676
1712
|
ErrorTypes2["KbWriteConflict"] = "KbWriteConflict";
|
|
1677
1713
|
return ErrorTypes2;
|
|
1678
1714
|
})(ErrorTypes || {});
|
|
@@ -1741,6 +1777,25 @@ var KbWriteConflictError = class extends BaseError {
|
|
|
1741
1777
|
}
|
|
1742
1778
|
conceptId;
|
|
1743
1779
|
};
|
|
1780
|
+
var KbSelfVerificationError = class extends BaseError {
|
|
1781
|
+
constructor(conceptId2, actor, generatedBy) {
|
|
1782
|
+
super({
|
|
1783
|
+
message: `kb: ${conceptId2} was generated by ${generatedBy}, and a record's generator cannot verify it \u2014 only a human or a different actor can`,
|
|
1784
|
+
errorType: "KbSelfVerification" /* KbSelfVerification */,
|
|
1785
|
+
code: 400,
|
|
1786
|
+
fault: "User" /* User */,
|
|
1787
|
+
retriable: false,
|
|
1788
|
+
reportToUser: true,
|
|
1789
|
+
details: { conceptId: conceptId2, actor, generatedBy, action: "refused" }
|
|
1790
|
+
});
|
|
1791
|
+
this.conceptId = conceptId2;
|
|
1792
|
+
this.actor = actor;
|
|
1793
|
+
this.generatedBy = generatedBy;
|
|
1794
|
+
}
|
|
1795
|
+
conceptId;
|
|
1796
|
+
actor;
|
|
1797
|
+
generatedBy;
|
|
1798
|
+
};
|
|
1744
1799
|
var KbInvalidConceptIdError = class extends BaseError {
|
|
1745
1800
|
constructor(message, details) {
|
|
1746
1801
|
super({
|
|
@@ -1971,6 +2026,40 @@ var KbStore = class {
|
|
|
1971
2026
|
{ operation: `status:${status}`, by: actor }
|
|
1972
2027
|
);
|
|
1973
2028
|
}
|
|
2029
|
+
/**
|
|
2030
|
+
* Appends one `verified[]` event: who checked the record, when, and what the
|
|
2031
|
+
* check found. Append-only — prior events are history, and are spread into
|
|
2032
|
+
* the new array untouched rather than reshaped through the write schema.
|
|
2033
|
+
*
|
|
2034
|
+
* A record's generator cannot verify its own record unless the actor is
|
|
2035
|
+
* human: the generator re-reading its own output is not an independent
|
|
2036
|
+
* check. The rule runs before the mutation so a refusal never publishes,
|
|
2037
|
+
* and the refusal is logged under its own operation name — `mutate` only
|
|
2038
|
+
* logs what it publishes.
|
|
2039
|
+
*/
|
|
2040
|
+
async verify(bundlePath2, conceptId2, note, actor = "unknown", at = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2041
|
+
const event = kbVerifiedEventSchema.parse({ by: actor, at, note });
|
|
2042
|
+
const existing = await this.read(bundlePath2, conceptId2);
|
|
2043
|
+
if (!existing) throw new KbRecordNotFoundError(conceptId2);
|
|
2044
|
+
const generatedBy = existing.frontmatter.generated?.by;
|
|
2045
|
+
if (generatedBy !== void 0 && actor.toLowerCase() === generatedBy.toLowerCase() && !normalizeActor(actor).startsWith("human:")) {
|
|
2046
|
+
await this.record(this.root(bundlePath2), {
|
|
2047
|
+
operation: "verify:refused",
|
|
2048
|
+
conceptId: conceptId2,
|
|
2049
|
+
by: actor
|
|
2050
|
+
});
|
|
2051
|
+
throw new KbSelfVerificationError(conceptId2, actor, generatedBy);
|
|
2052
|
+
}
|
|
2053
|
+
return this.mutate(
|
|
2054
|
+
bundlePath2,
|
|
2055
|
+
conceptId2,
|
|
2056
|
+
(frontmatter) => ({
|
|
2057
|
+
...frontmatter,
|
|
2058
|
+
verified: [...frontmatter.verified ?? [], event]
|
|
2059
|
+
}),
|
|
2060
|
+
{ operation: "verify", by: actor }
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
1974
2063
|
/**
|
|
1975
2064
|
* Marks `conceptId` superseded by `replacementId`, and links both directions.
|
|
1976
2065
|
*
|
|
@@ -2305,6 +2394,11 @@ function matches(record, needle) {
|
|
|
2305
2394
|
(field) => field?.toLowerCase().includes(needle)
|
|
2306
2395
|
);
|
|
2307
2396
|
}
|
|
2397
|
+
function normalizeActor(id) {
|
|
2398
|
+
const colon = id.indexOf(":");
|
|
2399
|
+
if (colon === -1) return id.toLowerCase();
|
|
2400
|
+
return id.slice(0, colon + 1).toLowerCase() + id.slice(colon + 1);
|
|
2401
|
+
}
|
|
2308
2402
|
function digest(contents) {
|
|
2309
2403
|
return createHash("sha256").update(contents).digest("hex");
|
|
2310
2404
|
}
|
|
@@ -2312,6 +2406,7 @@ function digest(contents) {
|
|
|
2312
2406
|
export {
|
|
2313
2407
|
kbSourceSchema,
|
|
2314
2408
|
kbActorStampSchema,
|
|
2409
|
+
kbVerifiedEventSchema,
|
|
2315
2410
|
kbAnchorSchema,
|
|
2316
2411
|
KB_RECORD_TYPES,
|
|
2317
2412
|
KB_SLUG_PATTERN,
|
|
@@ -2377,6 +2472,7 @@ export {
|
|
|
2377
2472
|
KbRecordAlreadyExistsError,
|
|
2378
2473
|
KbRecordNotFoundError,
|
|
2379
2474
|
KbWriteConflictError,
|
|
2475
|
+
KbSelfVerificationError,
|
|
2380
2476
|
KbInvalidConceptIdError,
|
|
2381
2477
|
SEARCH_INDEX_FILE,
|
|
2382
2478
|
searchBase,
|
|
@@ -2385,4 +2481,4 @@ export {
|
|
|
2385
2481
|
KB_DIR,
|
|
2386
2482
|
KbStore
|
|
2387
2483
|
};
|
|
2388
|
-
//# sourceMappingURL=chunk-
|
|
2484
|
+
//# sourceMappingURL=chunk-PNSRTKYN.js.map
|