bantamkit-mcp 0.29.1 → 0.30.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/eventlog.js CHANGED
@@ -32,10 +32,11 @@
32
32
  * one stray write breaks the wire suite. Nothing in this module names `process.stdout`,
33
33
  * `process.stderr` or `console`.
34
34
  * * **Metadata only.** Never a tool argument's value, never a memory body, never a
35
- * validated output, never a query, never a document row. Six of the eleven tools take
36
- * unbounded free text and five take absolute paths (`bantamkit_read`'s `path` is one and
37
- * `skill_audit`'s `root` is another; each record carries counts and tokens from a closed
38
- * set — never the path, never a part name, never a skill id).
35
+ * validated output, never a query, never a document row. Seven of the thirteen tools take
36
+ * unbounded free text and six take absolute paths (`bantamkit_read`'s `path` is one,
37
+ * `skill_audit`'s `root` is another and `repo_map`'s `root` and `focus` are the third;
38
+ * each record carries counts and tokens from a closed set — never the path, never a part
39
+ * name, never a skill id, never a mapped file).
39
40
  * Every value written here is an ASCII token from a closed set, a number, or a boolean.
40
41
  * * **Never `String(err)`.** Only the constructor name. This is not hypothetical: the host
41
42
  * itself has already persisted `input_value={'schema_path': '/Users/k...
package/dist/index.d.ts CHANGED
@@ -13,11 +13,13 @@ export type { PyOrder, YamlScalar, YamlValue } from './memory/pyyaml.js';
13
13
  export { countFacts, discoverProjectStore, loadGrants, MEMORY_DIR_ENV, PROJECT_STORE, resolveProjectStore, } from './memory/layers.js';
14
14
  export type { StoreBinding } from './memory/layers.js';
15
15
  export { layerLabel, Memory, normalizeName, profileStore } from './memory/component.js';
16
- export type { CompactOutcome, MemoryOptions, RecallOutcome, SaveOutcome } from './memory/component.js';
16
+ export type { CompactOutcome, DreamOutcome, MemoryOptions, RecallOutcome, SaveOutcome, } from './memory/component.js';
17
+ export { absolutise, blockKey, claimSlot, collapse, dream, formatFixed3, mergeBodies, mergeDescriptions, mergeLinks, PROFILE_LAYER, PROJECT_LAYER, pyIntDigits, sortedNames, splitBlocks, SUPERSEDED_HEADING, } from './memory/dream.js';
18
+ export type { DateHit, DreamMerge, DreamResult, SimilarPair, Superseded } from './memory/dream.js';
17
19
  export { CAP_BYTES, DEFAULT_RELATIVE_PATH, defaultPath, encodeRecord, EVENT_LOG_ENV, EventLog, formatTimestamp, resolvePath, SCHEMA_VERSION, } from './eventlog.js';
18
20
  export type { DetailValue } from './eventlog.js';
19
- export { DEFAULT_INDEX_BUDGET, DUPLICATE_JACCARD, jaccard, MemoryBudgetExceeded, MemoryStore, MemoryValidationError, pyCompareLt, pyEqualValue, pyHashKey, pyText, sortScored, tokens, VALID_TYPES, } from './memory/store.js';
20
- export type { MemoryStoreOptions, SaveResult } from './memory/store.js';
21
+ export { DEFAULT_INDEX_BUDGET, DUPLICATE_JACCARD, jaccard, MemoryBudgetExceeded, MemoryStore, MemoryValidationError, pyCompareLt, pyEqualValue, pyHashKey, pyText, RECALL_MIN_SCORE_RATIO, sortScored, tokens, VALID_TYPES, } from './memory/store.js';
22
+ export type { MemoryStoreOptions, SaveResult, StoreInternals } from './memory/store.js';
21
23
  export { asPyOSError, cmpCodepoint, matchesMd, normcase, pyDecodeUtf8, pyCwd, pyExists, pyExpanduser, pyHome, pyIsAbsolute, pyIsDir, pyIsFile, pyJoin, pyLexists, pyMkdirParents, pyMtimeDate, pyName, PyOSError, pyParent, pyParents, pyReadText, pyReplace, pyRepr, pyResolve, PyRuntimeError, pyScandirNames, PyUnicodeDecodeError, pyAppendText, pyStatIsDir, pySuffix, pyUnlink, pyWithSuffix, pyWriteText, sortedPathNames, sortedPathParts, STRERROR_NAMES, } from './memory/pyfs.js';
22
24
  export { clockIn, clockOut, HISTORY_RING_SIZE, SCHEMA_NAME, status, TERMINAL_UNIT_STATUS, timestamp, } from './shiftwork.js';
23
25
  export type { ClockOptions } from './shiftwork.js';
package/dist/index.js CHANGED
@@ -7,7 +7,8 @@ export { decodeFactBytes, FactParseError, formatFact, parseFactText, parseFrontm
7
7
  export { constructPlain, PyScalar, resolveImplicitTag, safeDumpMapping, YamlConstructError, YamlEmitError, } from './memory/pyyaml.js';
8
8
  export { countFacts, discoverProjectStore, loadGrants, MEMORY_DIR_ENV, PROJECT_STORE, resolveProjectStore, } from './memory/layers.js';
9
9
  export { layerLabel, Memory, normalizeName, profileStore } from './memory/component.js';
10
+ export { absolutise, blockKey, claimSlot, collapse, dream, formatFixed3, mergeBodies, mergeDescriptions, mergeLinks, PROFILE_LAYER, PROJECT_LAYER, pyIntDigits, sortedNames, splitBlocks, SUPERSEDED_HEADING, } from './memory/dream.js';
10
11
  export { CAP_BYTES, DEFAULT_RELATIVE_PATH, defaultPath, encodeRecord, EVENT_LOG_ENV, EventLog, formatTimestamp, resolvePath, SCHEMA_VERSION, } from './eventlog.js';
11
- export { DEFAULT_INDEX_BUDGET, DUPLICATE_JACCARD, jaccard, MemoryBudgetExceeded, MemoryStore, MemoryValidationError, pyCompareLt, pyEqualValue, pyHashKey, pyText, sortScored, tokens, VALID_TYPES, } from './memory/store.js';
12
+ export { DEFAULT_INDEX_BUDGET, DUPLICATE_JACCARD, jaccard, MemoryBudgetExceeded, MemoryStore, MemoryValidationError, pyCompareLt, pyEqualValue, pyHashKey, pyText, RECALL_MIN_SCORE_RATIO, sortScored, tokens, VALID_TYPES, } from './memory/store.js';
12
13
  export { asPyOSError, cmpCodepoint, matchesMd, normcase, pyDecodeUtf8, pyCwd, pyExists, pyExpanduser, pyHome, pyIsAbsolute, pyIsDir, pyIsFile, pyJoin, pyLexists, pyMkdirParents, pyMtimeDate, pyName, PyOSError, pyParent, pyParents, pyReadText, pyReplace, pyRepr, pyResolve, PyRuntimeError, pyScandirNames, PyUnicodeDecodeError, pyAppendText, pyStatIsDir, pySuffix, pyUnlink, pyWithSuffix, pyWriteText, sortedPathNames, sortedPathParts, STRERROR_NAMES, } from './memory/pyfs.js';
13
14
  export { clockIn, clockOut, HISTORY_RING_SIZE, SCHEMA_NAME, status, TERMINAL_UNIT_STATUS, timestamp, } from './shiftwork.js';
@@ -31,7 +31,7 @@ import { type PyValue } from '../pyjson.js';
31
31
  /** What a parameter accepts. `optional` is `| None = None` in the signature. */
32
32
  export interface FieldSpec {
33
33
  readonly name: string;
34
- readonly kind: 'str' | 'int' | 'dict' | 'dictInt' | 'dictStr' | 'listStr';
34
+ readonly kind: 'str' | 'int' | 'bool' | 'dict' | 'dictInt' | 'dictStr' | 'listStr';
35
35
  readonly optional: boolean;
36
36
  /** `Field(le=...)`: an inclusive ceiling, checked AFTER the lax int parse succeeds. */
37
37
  readonly le?: bigint;
@@ -41,6 +41,25 @@ export interface ArgModel {
41
41
  readonly model: string;
42
42
  readonly fields: readonly FieldSpec[];
43
43
  }
44
+ /**
45
+ * `assets/tools/bantamkit_read.json` `offset.maximum` — `Number.MAX_SAFE_INTEGER`, the
46
+ * largest integer `JSON.parse` reads back unchanged (F1). The reference binds it as
47
+ * `Annotated[int, Field(le=OFFSET_MAXIMUM)]`, so above it the refusal is pydantic's
48
+ * `less_than_equal` frame and the handler never runs; this side reads the JSON with its
49
+ * own decoder, so `9007199254740993` arrives exact and is refused with the same text.
50
+ *
51
+ * EXPORTED so the number can be TIED to the asset rather than copied a fourth time.
52
+ * `assets/tools/bantamkit_read.json` is the published contract and this is the number the
53
+ * signature enforces; `test/document-cache-and-bounds.test.mjs` reads that file OFF DISK and
54
+ * compares it against this constant, against the schema the server actually serves, and
55
+ * against what the handler refuses — the port of `runtime-py`'s
56
+ * `tests/test_document_manifest_parity.py`. Register entries (c) and (l),
57
+ * `docs/roadmap-toolbox.md` row 8. It stays a test rather than a runtime read for the
58
+ * reference's reason: a module-level asset load would make importing this module fail
59
+ * wherever the asset pack is not on disk, which is a worse failure than a refusal to
60
+ * register a tool.
61
+ */
62
+ export declare const OFFSET_MAXIMUM = 9007199254740991n;
44
63
  /**
45
64
  * One model per handler, mirroring `build_server`'s closures signature for signature.
46
65
  *
@@ -41,8 +41,19 @@ const opt = (name, kind, bound = {}) => ({
41
41
  * `Annotated[int, Field(le=OFFSET_MAXIMUM)]`, so above it the refusal is pydantic's
42
42
  * `less_than_equal` frame and the handler never runs; this side reads the JSON with its
43
43
  * own decoder, so `9007199254740993` arrives exact and is refused with the same text.
44
+ *
45
+ * EXPORTED so the number can be TIED to the asset rather than copied a fourth time.
46
+ * `assets/tools/bantamkit_read.json` is the published contract and this is the number the
47
+ * signature enforces; `test/document-cache-and-bounds.test.mjs` reads that file OFF DISK and
48
+ * compares it against this constant, against the schema the server actually serves, and
49
+ * against what the handler refuses — the port of `runtime-py`'s
50
+ * `tests/test_document_manifest_parity.py`. Register entries (c) and (l),
51
+ * `docs/roadmap-toolbox.md` row 8. It stays a test rather than a runtime read for the
52
+ * reference's reason: a module-level asset load would make importing this module fail
53
+ * wherever the asset pack is not on disk, which is a worse failure than a refusal to
54
+ * register a tool.
44
55
  */
45
- const OFFSET_MAXIMUM = 9007199254740991n;
56
+ export const OFFSET_MAXIMUM = 9007199254740991n;
46
57
  /**
47
58
  * One model per handler, mirroring `build_server`'s closures signature for signature.
48
59
  *
@@ -115,6 +126,28 @@ export const ARG_MODELS = {
115
126
  // `dict[str, str]`, and the reason `dictStr` exists beside it: a `str` field is STRICT even
116
127
  // in lax mode, so `{'a': 1}` is `versions.a` / `string_type` where the same value under
117
128
  // `usage` would validate.
129
+ // `dry_run: bool | None = None` on the reference (job45 row 5). LAX bool, and every arm
130
+ // below was MEASURED against the real stdio server, not read off pydantic's docs
131
+ // (`scratchpad` probe, 33 inputs): `0`/`1` and `0.0`/`1.0` coerce; any OTHER integral
132
+ // number — `2`, `-1`, `2.0` — is `bool_parsing`; a FRACTIONAL float is `bool_type`, a
133
+ // different frame from the integral one and the reason the float arm is split; a string
134
+ // is matched case-insensitively against two closed sets and is NOT stripped first, so
135
+ // `'true'` validates and `' true '` is `bool_parsing`; `''` is `bool_parsing`; a list or a
136
+ // dict is `bool_type`; and an explicit `null` is the default.
137
+ memory_dream: { model: 'memory_dreamArguments', fields: [opt('dry_run', 'bool')] },
138
+ // `repo_map(root: str, focus: list[str] | None = None, budget: int | None = None)` on
139
+ // the reference (job45 row 10). `root` is the strict `str` every other string field is —
140
+ // `123` is `string_type`. `focus` is the same `list[str]` as `skill_audit`'s `enabled`,
141
+ // so a non-list is `list_type` and a list holding a non-string is `focus.0` /
142
+ // `string_type`. `budget` is the lax `int` that `k`, `reserve` and `limit` are: `'2'`,
143
+ // `True` and `3.0` validate, `'2.5'` is `int_parsing`, `2.5` is `int_from_float`, and an
144
+ // explicit `null` is the default. No `le`, because the reference binds none — the
145
+ // manifest's `minimum: 0` is advisory to the client and the handler's own refusal is the
146
+ // negative one, exactly as `skill_audit`'s `budget` works.
147
+ repo_map: {
148
+ model: 'repo_mapArguments',
149
+ fields: [req('root', 'str'), opt('focus', 'listStr'), opt('budget', 'int')],
150
+ },
118
151
  skill_audit: {
119
152
  model: 'skill_auditArguments',
120
153
  fields: [
@@ -167,6 +200,13 @@ function truncateRepr(text) {
167
200
  tail += 1;
168
201
  return `${decoder.decode(bytes.slice(0, head))}...${decoder.decode(bytes.slice(tail))}`;
169
202
  }
203
+ /**
204
+ * The two closed sets pydantic-core matches a string against for a lax `bool`, lowercased.
205
+ *
206
+ * No stripping: `' true '` is `bool_parsing` on the reference, measured over the wire.
207
+ */
208
+ const BOOL_TRUE = new Set(['1', 'on', 't', 'true', 'y', 'yes']);
209
+ const BOOL_FALSE = new Set(['0', 'off', 'f', 'false', 'n', 'no']);
170
210
  /** i64, which is the width pydantic parses an int into before it complains about size. */
171
211
  const I64_MAX = 9223372036854775807n;
172
212
  const I64_MIN = -9223372036854775808n;
@@ -247,6 +287,45 @@ function checkField(spec, value) {
247
287
  }
248
288
  return bad.length > 0 ? bad : { value };
249
289
  }
290
+ case 'bool': {
291
+ const parsing = () => [
292
+ {
293
+ loc: spec.name,
294
+ type: 'bool_parsing',
295
+ msg: 'Input should be a valid boolean, unable to interpret input',
296
+ input: value,
297
+ },
298
+ ];
299
+ const wrongType = () => [
300
+ { loc: spec.name, type: 'bool_type', msg: 'Input should be a valid boolean', input: value },
301
+ ];
302
+ if (value.t === 'bool')
303
+ return { value };
304
+ if (value.t === 'int') {
305
+ if (value.v === 0n || value.v === 1n)
306
+ return { value: { t: 'bool', v: value.v === 1n } };
307
+ return parsing();
308
+ }
309
+ if (value.t === 'float') {
310
+ // A FRACTIONAL float is `bool_type` and an out-of-range integral one is
311
+ // `bool_parsing`. Measured, and not a distinction anyone would guess: `0.5` and
312
+ // `2.0` are both floats and they refuse under different frames.
313
+ if (!Number.isInteger(value.v))
314
+ return wrongType();
315
+ if (value.v === 0 || value.v === 1)
316
+ return { value: { t: 'bool', v: value.v === 1 } };
317
+ return parsing();
318
+ }
319
+ if (value.t === 'str') {
320
+ const key = value.v.toLowerCase();
321
+ if (BOOL_TRUE.has(key))
322
+ return { value: { t: 'bool', v: true } };
323
+ if (BOOL_FALSE.has(key))
324
+ return { value: { t: 'bool', v: false } };
325
+ return parsing();
326
+ }
327
+ return wrongType();
328
+ }
250
329
  case 'int': {
251
330
  const checked = checkInt(spec.name, value);
252
331
  if (!('value' in checked))
@@ -1,17 +1,19 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import * as repomap from '../repomap.js';
2
3
  import { EventLog } from '../eventlog.js';
3
4
  import type { Memory } from '../memory/component.js';
4
5
  import type { RawStdioTransport } from './transport.js';
5
6
  /**
6
- * The eleven, in the order `build_server` lists them — which is the order `tools/list` emits.
7
+ * The thirteen, in the order `build_server` lists them — which is the order `tools/list` emits.
7
8
  *
8
9
  * `bantamkit_status` went LAST rather than first, `memory_compact` after it rather than
9
- * beside `memory_save` where a reader would look for it, `bantamkit_read` after that and
10
- * `skill_audit` after that, for the same reason the reference appends all four: registration
11
- * order IS the served order, and appending is the only edit that leaves the other ten where
12
- * every existing declaration says they are.
10
+ * beside `memory_save` where a reader would look for it, `bantamkit_read` after that,
11
+ * `skill_audit` after that, `memory_dream` after that and `repo_map` after that, for the
12
+ * same reason the reference appends all six: registration order IS the served order, and
13
+ * appending is the only edit that leaves the other twelve where every existing declaration
14
+ * says they are.
13
15
  */
14
- export declare const MCP_TOOLS: readonly ["memory_save", "memory_recall", "validate_json", "shiftwork_clock_in", "shiftwork_clock_out", "shiftwork_status", "build_identity", "bantamkit_status", "memory_compact", "bantamkit_read", "skill_audit"];
16
+ export declare const MCP_TOOLS: readonly ["memory_save", "memory_recall", "validate_json", "shiftwork_clock_in", "shiftwork_clock_out", "shiftwork_status", "build_identity", "bantamkit_status", "memory_compact", "bantamkit_read", "skill_audit", "memory_dream", "repo_map"];
15
17
  /**
16
18
  * The advertisement for one tool, out of its manifest, with the surface checked.
17
19
  *
@@ -28,6 +30,34 @@ export declare const MCP_TOOLS: readonly ["memory_save", "memory_recall", "valid
28
30
  * that detects drift. Registered here, not fixed here; `runtime-py` is the reference.
29
31
  */
30
32
  export declare function fromManifest(name: string): Record<string, unknown>;
33
+ /**
34
+ * The last paragraph of every `repo_map` reply, refusal excepted. FIXED AND MANDATORY, and
35
+ * byte-identical to the reference's `REPO_MAP_TAIL`.
36
+ *
37
+ * Roadmap row 10's build gate was "build only after #4 shows discovery tokens dominate",
38
+ * and #4 REFUTED it: discovery is 0.114 % of real prompt tokens because 97.8 % of the bill
39
+ * is `cache_read`. The feature ships on an explicit ruling to build it anyway, as a
40
+ * PRECISION feature. A surface that let a caller believe the map is a token saving would
41
+ * say the one thing the measurement forbids, so the refutation travels with every answer
42
+ * rather than living only in a doc nobody reads at call time.
43
+ *
44
+ * The second sentence is the budget's unit, for the same reason: `DEFAULT_BUDGET = 4000` is
45
+ * "1 K tokens" only at the char/4 convention, whose error bar is unmeasured because
46
+ * measuring it needs the tokenizer the pure-node ruling forbids.
47
+ */
48
+ export declare const REPO_MAP_TAIL: string;
49
+ /** What a listing says when there is nothing to list. The reference's `REPO_MAP_EMPTY`. */
50
+ export declare const REPO_MAP_EMPTY = "(nothing listed: no file under this root scanned into a definition)";
51
+ /**
52
+ * The `repo_map` tool's prose, byte for byte, from the structured result.
53
+ *
54
+ * Split out of the handler for the same reason the reference splits `repo_map_reply` out of
55
+ * its own: one shape to reproduce rather than a format string buried in a `case`, and the
56
+ * `repomap` conformance suite compares the rendered reply without standing up a server. No
57
+ * float is ever rendered here — `repomap`'s trap (8); every number on the header line is an
58
+ * integer, and `String(1.0)` is `'1'` where `repr(1.0)` is `'1.0'`.
59
+ */
60
+ export declare function repoMapReply(result: repomap.RepoMap): string;
31
61
  /**
32
62
  * Assemble the MCP server around one Memory instance (the per-person state).
33
63
  *