bantamkit-mcp 0.26.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.
Files changed (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +398 -0
  3. package/assets/contracts/default.yaml +90 -0
  4. package/assets/evals/devteam/manifest.yaml +351 -0
  5. package/assets/evals/devteam/repo/HISTORY.md +18 -0
  6. package/assets/evals/devteam/repo/README.md +12 -0
  7. package/assets/evals/devteam/repo/docs/architecture.md +17 -0
  8. package/assets/evals/devteam/repo/docs/runbook.md +10 -0
  9. package/assets/evals/devteam/repo/issues/142-settlement-timeout.md +23 -0
  10. package/assets/evals/devteam/repo/patches/0009-retry-budget.patch +38 -0
  11. package/assets/evals/devteam/repo/src/ledger/__init__.py +3 -0
  12. package/assets/evals/devteam/repo/src/ledger/config.py +35 -0
  13. package/assets/evals/devteam/repo/src/ledger/errors.py +13 -0
  14. package/assets/evals/devteam/repo/src/ledger/posting.py +12 -0
  15. package/assets/evals/devteam/repo/src/ledger/registry.py +7 -0
  16. package/assets/evals/devteam/repo/src/ledger/report.py +9 -0
  17. package/assets/evals/devteam/repo/src/ledger/retry.py +17 -0
  18. package/assets/evals/devteam/repo/src/ledger/settle.py +16 -0
  19. package/assets/evals/devteam/repo/src/ledger/validate.py +14 -0
  20. package/assets/evals/devteam/repo/tests/test_posting.py +13 -0
  21. package/assets/evals/devteam/repo/tests/test_settle.py +9 -0
  22. package/assets/evals/devteam/tasks/dt-error-contract.yaml +186 -0
  23. package/assets/evals/devteam/tasks/dt-handler-map.yaml +183 -0
  24. package/assets/evals/devteam/tasks/dt-patch-before-after.yaml +182 -0
  25. package/assets/evals/devteam/tasks/dt-retry-attempts.yaml +181 -0
  26. package/assets/evals/devteam/tasks/dt-settlement-config.yaml +185 -0
  27. package/assets/evals/devteam/tasks/dt-symbol-home.yaml +181 -0
  28. package/assets/evals/devteam/tasks/dt-trace-blame.yaml +182 -0
  29. package/assets/evals/devteam/tasks/dt-unread-key.yaml +180 -0
  30. package/assets/evals/document/tasks/doc-large-in-137.yaml +38 -0
  31. package/assets/evals/document/tasks/doc-large-in-359.yaml +44 -0
  32. package/assets/evals/document/tasks/doc-large-in-372.yaml +38 -0
  33. package/assets/evals/document/tasks/doc-large-out-11764.yaml +37 -0
  34. package/assets/evals/document/tasks/doc-large-out-4137.yaml +37 -0
  35. package/assets/evals/document/tasks/doc-large-out-8022.yaml +37 -0
  36. package/assets/evals/document/tasks/doc-small-137.yaml +37 -0
  37. package/assets/evals/document/tasks/doc-small-261.yaml +37 -0
  38. package/assets/evals/document/tasks/doc-small-388.yaml +37 -0
  39. package/assets/evals/fixtures/.gitkeep +0 -0
  40. package/assets/evals/fixtures/catalog.json +6 -0
  41. package/assets/evals/perturbations/task-completion.yaml +576 -0
  42. package/assets/evals/tasks/.gitkeep +0 -0
  43. package/assets/evals/tasks/extract-contact.yaml +14 -0
  44. package/assets/evals/tasks/extract-invoice.yaml +14 -0
  45. package/assets/evals/tasks/extract-order.yaml +15 -0
  46. package/assets/evals/tasks/extract-schedule.yaml +14 -0
  47. package/assets/evals/tasks/extract-versions.yaml +17 -0
  48. package/assets/evals/tasks/nav-prod-port.yaml +84 -0
  49. package/assets/evals/tasks/nav-release-bundle.yaml +87 -0
  50. package/assets/evals/tasks/recall-audit-retention.yaml +17 -0
  51. package/assets/evals/tasks/recall-cache-ttl.yaml +13 -0
  52. package/assets/evals/tasks/recall-db-port.yaml +17 -0
  53. package/assets/evals/tasks/recall-deploy.yaml +13 -0
  54. package/assets/evals/tasks/recall-env-endpoint.yaml +18 -0
  55. package/assets/evals/tasks/recall-oncall-rotation.yaml +21 -0
  56. package/assets/evals/tasks/recall-oncall.yaml +13 -0
  57. package/assets/evals/tasks/recall-org-quota.yaml +18 -0
  58. package/assets/evals/tasks/recall-owner.yaml +13 -0
  59. package/assets/evals/tasks/shop-basket-total.yaml +10 -0
  60. package/assets/evals/tasks/shop-cheapest.yaml +9 -0
  61. package/assets/evals/tasks/shop-compare.yaml +9 -0
  62. package/assets/evals/tasks/shop-gadget-value.yaml +9 -0
  63. package/assets/evals/tasks/shop-stock-total.yaml +9 -0
  64. package/assets/evals/tasks/shop-total.yaml +9 -0
  65. package/assets/profiles/default.yaml +31 -0
  66. package/assets/profiles/patient.yaml +31 -0
  67. package/assets/rubrics/.gitkeep +0 -0
  68. package/assets/rubrics/code-quality.yaml +20 -0
  69. package/assets/rubrics/grounded-completion.yaml +37 -0
  70. package/assets/rubrics/task-completion.yaml +28 -0
  71. package/assets/schemas/shiftwork-checkpoint.json +188 -0
  72. package/assets/skills/.gitkeep +0 -0
  73. package/assets/skills/file-graph.md +7 -0
  74. package/assets/skills/memory.md +35 -0
  75. package/assets/tools/.gitkeep +0 -0
  76. package/assets/tools/bantamkit_read.json +48 -0
  77. package/assets/tools/bantamkit_status.json +25 -0
  78. package/assets/tools/build_identity.json +17 -0
  79. package/assets/tools/document_list.json +12 -0
  80. package/assets/tools/document_read.json +31 -0
  81. package/assets/tools/file_graph.json +12 -0
  82. package/assets/tools/memory_compact.json +31 -0
  83. package/assets/tools/memory_recall.json +38 -0
  84. package/assets/tools/memory_save.json +61 -0
  85. package/assets/tools/shiftwork_clock_in.json +25 -0
  86. package/assets/tools/shiftwork_clock_out.json +60 -0
  87. package/assets/tools/shiftwork_status.json +25 -0
  88. package/assets/tools/skill_audit.json +70 -0
  89. package/assets/tools/validate_json.json +31 -0
  90. package/dist/assets.d.ts +48 -0
  91. package/dist/assets.js +109 -0
  92. package/dist/charsets.d.ts +17 -0
  93. package/dist/charsets.js +580 -0
  94. package/dist/cli.d.ts +12 -0
  95. package/dist/cli.js +280 -0
  96. package/dist/contract.d.ts +145 -0
  97. package/dist/contract.js +608 -0
  98. package/dist/docread.d.ts +357 -0
  99. package/dist/docread.js +3315 -0
  100. package/dist/errors.d.ts +11 -0
  101. package/dist/errors.js +14 -0
  102. package/dist/eventlog.d.ts +137 -0
  103. package/dist/eventlog.js +294 -0
  104. package/dist/htmlentities.d.ts +2 -0
  105. package/dist/htmlentities.js +16 -0
  106. package/dist/index.d.ts +23 -0
  107. package/dist/index.js +13 -0
  108. package/dist/mcp/identity.d.ts +25 -0
  109. package/dist/mcp/identity.js +275 -0
  110. package/dist/mcp/pyargs.d.ts +80 -0
  111. package/dist/mcp/pyargs.js +362 -0
  112. package/dist/mcp/sdkjson.d.ts +56 -0
  113. package/dist/mcp/sdkjson.js +91 -0
  114. package/dist/mcp/server.d.ts +45 -0
  115. package/dist/mcp/server.js +740 -0
  116. package/dist/mcp/status.d.ts +210 -0
  117. package/dist/mcp/status.js +357 -0
  118. package/dist/mcp/transport.d.ts +68 -0
  119. package/dist/mcp/transport.js +111 -0
  120. package/dist/mcpreport.d.ts +214 -0
  121. package/dist/mcpreport.js +758 -0
  122. package/dist/memory/cli.d.ts +2 -0
  123. package/dist/memory/cli.js +396 -0
  124. package/dist/memory/component.d.ts +256 -0
  125. package/dist/memory/component.js +497 -0
  126. package/dist/memory/factfile.d.ts +120 -0
  127. package/dist/memory/factfile.js +406 -0
  128. package/dist/memory/layers.d.ts +92 -0
  129. package/dist/memory/layers.js +410 -0
  130. package/dist/memory/pyfs.d.ts +521 -0
  131. package/dist/memory/pyfs.js +1608 -0
  132. package/dist/memory/pyyaml.d.ts +155 -0
  133. package/dist/memory/pyyaml.js +868 -0
  134. package/dist/memory/store.d.ts +478 -0
  135. package/dist/memory/store.js +1034 -0
  136. package/dist/pyargparse.d.ts +141 -0
  137. package/dist/pyargparse.js +813 -0
  138. package/dist/pyjson.d.ts +110 -0
  139. package/dist/pyjson.js +524 -0
  140. package/dist/pyjsonschema.d.ts +50 -0
  141. package/dist/pyjsonschema.js +951 -0
  142. package/dist/shiftwork.d.ts +60 -0
  143. package/dist/shiftwork.js +324 -0
  144. package/dist/skillaudit.d.ts +172 -0
  145. package/dist/skillaudit.js +914 -0
  146. package/dist/statusline.d.ts +106 -0
  147. package/dist/statusline.js +281 -0
  148. package/package.json +45 -0
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "skill_audit",
3
+ "description": "Price the skill catalogue every session pays for, and name the collisions in it. A skill's `description:` frontmatter is loaded into the agent's context in EVERY session; its body is read only when the skill is invoked, so the descriptions are the standing bill. Walks `root` for `<marketplace>/<plugin>/<version>/skills/<name>/SKILL.md` and answers one JSON document: `roots` (what was scanned), `skills` (how many were counted), `catalogue_bytes` (UTF-8 bytes of their `description:` values), `findings` and `omissions`. Counted skills plus omissions account for every SKILL.md found — an omission is `{subject, count, size, what}` with subject one of `plugin-not-enabled`, `duplicate-skill`, `stale-version`, `unreadable-file`, `unparsed-frontmatter`, and `what` naming the paths. Pass `enabled` with the plugin ids the host has switched on (`<plugin>@<marketplace>`, as settings.json spells them): the plugin cache also holds disabled plugins and stale version directories, and counting those inflated a real audit to 41 skills / 19,065 B against a true 34 / 14,515. Identity comes from the path: `<marketplace>/<plugin>/<version>/skills/<name>/SKILL.md` relative to `root`; any other shape is a skill outside a plugin, identified by its directory name alone and never excluded by `enabled`. Exactly ONE version directory is resolved per (marketplace, plugin) BEFORE anything is counted, and its skills are that plugin's skills: a name absent from the resolved version is absent, never merged in from another version directory. The winner is the version directory name that sorts LAST in byte order — a byte order and deliberately not a semver comparison, so two runtimes cannot disagree about which directory was read, and so that names which are not versions at all still have a total order (a real cache spells nine of one plugin's directories as content hashes plus the literal `unknown`). What byte order gets wrong is stated rather than hidden: `10.0.0` loses to `9.0.0`, and among non-version names the winner is deterministic but arbitrary; both losers are named in an omission record. Pass `versions` with the version directory the host actually serves, per `<plugin>@<marketplace>` — the `installPath` recorded in the same `installed_plugins.json` the caller reads `enabled` out of — and that plugin is resolved to the named directory whatever byte order would have said; a plugin absent from `versions` falls back to byte order. It is host truth the caller supplies, exactly as `enabled` is, and it is not refused when it names a plugin or a directory this `root` does not hold: a plugin with no version directory here is not resolved at all, and a directory that is not here means every directory that IS here loses and is named in a `stale-version` record. Measured 2026-09-05 on a real cache, byte order picks `unknown` for a plugin whose host serves `1dd995193ba2`, so the installed copy is discarded as a duplicate. The candidates are the version DIRECTORIES on disk — every path spelled `<marketplace>/<plugin>/<version>/skills` — and not the skills that survived reading: a version directory holding no `SKILL.md`, or only files that will not decode or will not parse, still exists and the host still serves it, so an older directory must not win by default. Every SKILL.md under a version directory that did not win is omitted, under one of two subjects: `duplicate-skill` when the resolved version has a skill of the same name standing in for it, `stale-version` when it does not — a skill the resolved version DROPPED, which is on disk, in nobody's bill, and would otherwise be resurrected by the audit. Deduping by the (marketplace, plugin, name) triple instead merges versions rather than choosing between them, and measured 2026-09-05 over a real cache holding a plugin's `0.4.0` (14 skills) and `0.5.0` (5, nine having moved to another plugin) it answered 31 skills / 9,280 bytes where the host serves 22 / 3,396. Inside the resolved version `duplicate-skill` still covers two files claiming the same (marketplace, plugin, name), which is reachable only for skills outside a plugin; the last in scan order wins. Every omission carries the bytes it would have added. A skill is identified as `<plugin>:<name>`, the spelling the host uses and the key `usage` is looked up by, or as `<name>` when it has no plugin. A `description:` folded over several lines is joined with single spaces before it is counted or scanned, and a value written as a whole-value quoted YAML scalar is UNWRAPPED after that fold and before either — the host's parser strips those quotes before a session sees the description, so they are not `catalogue_bytes`, and only quotes INSIDE the value delimit a trigger phrase. The unwrap applies to every frontmatter key, not just `description:`, so `name: \"s\"` is the name `s` and `router: \"true\"` is a router. A value is a whole-value scalar only when it begins with `\"` or `'` AND that opening quote's own closing quote is the value's LAST character, judged by YAML's two escape rules and no others: inside a `\"` scalar a backslash escapes the character after it, so `\\\"` does not close it and the sequences `\\\"` and `\\\\` resolve to `\"` and `\\` while every other `\\x` is left exactly as written; inside a `'` scalar a doubled `''` is one literal apostrophe, so it does not close it and resolves to `'`. Every other value is left as written and scanned literally: `\"a\" and \"b\"` begins and ends with `\"` and is NOT one scalar, because its opening quote closes at index 2, and stripping it would weld the value into one junk phrase and destroy both real ones. A value that OPENS with a quote and never closes one — including one whose last quote is escaped — is a LITERAL: it is not unwrapped, its quote character is counted, its unpaired quote opens no phrase, and it is not a `frontmatter-malformed` finding, which names failures of the BLOCK and not of one value. Measured 2026-09-05 over a real plugin cache: 17 of 31 enabled skills write `description: \"...\"`, so a reader that takes those quotes literally over-prices the majority of a catalogue and buries any phrase quoted inside them. Five findings, each `{kind, severity, skills, detail}`, severity fixed per kind: `shared-trigger-phrase` (high) — two or more skills quote the same literal phrase, so which one fires is a coin flip; `catalogue-over-budget` (high) — `catalogue_bytes` exceeds `budget`; `never-invoked` (low) — a counted skill has zero calls in `usage`; `frontmatter-malformed` (medium) — no frontmatter block, an unterminated one, or no `description:` key, where a block that cannot be parsed at all is also omitted as `unparsed-frontmatter` and adds nothing to `skills` while a parsed block with no description is a skill costing zero bytes; `name-mismatch` (medium) — the frontmatter `name:` is not the skill's directory name. Collisions are EXACT matches on literal quoted phrases and never a similarity score: Jaccard over description tokens was measured across 820 real pairs and refuted, topping out at 0.239 and ranking the two real collisions 118th and 791st of 820. Phrases are the text of the UNWRAPPED value between `\"` pairs, or between `'` pairs where the quote is not flanked by letters — `'don't'` is a contraction, not a phrase — and a phrase with no letter or digit in it is ignored. A router, a skill whose frontmatter carries `router: true`, quotes its siblings' phrases by design and is left out of the phrase index entirely. Four argument failures are refused with their own sentences and never as a partial answer: an unknown `check`, a negative `budget`, a `root` that is missing or is a file, and an EMPTY `root` — which passes JSON-schema `string` and would otherwise resolve to the server's own working directory. Nothing about the CONTENT of the tree refuses. Deterministic: the tool reads nothing but `root`. The three facts a directory of files cannot answer arrive from the caller — call counts in `usage` (`node tools/ledger/tool-usage.mjs --group skill --json`), switched-on plugins in `enabled`, served version directories in `versions` — never by reading the host's transcripts, settings or plugin registry.",
4
+ "surfaces": [
5
+ "mcp"
6
+ ],
7
+ "parameters": {
8
+ "type": "object",
9
+ "required": [
10
+ "root"
11
+ ],
12
+ "properties": {
13
+ "root": {
14
+ "type": "string",
15
+ "description": "Directory to scan for SKILL.md files, absolute or relative to the server's working directory. It must name a directory: the empty string is refused rather than resolved, because resolving it would audit whatever directory the server happens to be standing in"
16
+ },
17
+ "enabled": {
18
+ "type": "array",
19
+ "items": {
20
+ "type": "string"
21
+ },
22
+ "description": "Plugin ids that are switched on, `<plugin>@<marketplace>`; when given only skills under those plugins are counted, when omitted every skill under `root` counts"
23
+ },
24
+ "usage": {
25
+ "type": "object",
26
+ "additionalProperties": {
27
+ "type": "integer",
28
+ "minimum": 0
29
+ },
30
+ "description": "Skill id (`<plugin>:<name>`) to call count, the caller's own measurement; a counted skill absent from a supplied map is treated as zero"
31
+ },
32
+ "check": {
33
+ "type": "string",
34
+ "enum": [
35
+ "all",
36
+ "phrase",
37
+ "budget",
38
+ "frontmatter"
39
+ ],
40
+ "description": "Which family of findings to report; default `all`. `skills`, `catalogue_bytes` and `omissions` are reported whatever this says"
41
+ },
42
+ "budget": {
43
+ "type": "integer",
44
+ "minimum": 0,
45
+ "maximum": 9007199254740991,
46
+ "description": "Catalogue byte budget; `catalogue-over-budget` is only reported when this is given"
47
+ },
48
+ "versions": {
49
+ "type": "object",
50
+ "additionalProperties": {
51
+ "type": "string"
52
+ },
53
+ "description": "Plugin id (`<plugin>@<marketplace>`) to the version directory name the host actually serves; when a plugin is named here that directory is resolved whatever byte order would have said, and a plugin absent from it falls back to byte order"
54
+ }
55
+ }
56
+ },
57
+ "output_schema": {
58
+ "properties": {
59
+ "result": {
60
+ "title": "Result",
61
+ "type": "string"
62
+ }
63
+ },
64
+ "required": [
65
+ "result"
66
+ ],
67
+ "type": "object",
68
+ "title": "skill_auditOutput"
69
+ }
70
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "validate_json",
3
+ "description": "Validate candidate output text against a JSON Schema. Returns {valid, feedback}; when invalid, feed the feedback back to the model and retry.",
4
+ "surfaces": [
5
+ "mcp"
6
+ ],
7
+ "parameters": {
8
+ "properties": {
9
+ "output": {
10
+ "title": "Output",
11
+ "type": "string"
12
+ },
13
+ "schema": {
14
+ "additionalProperties": true,
15
+ "title": "Schema",
16
+ "type": "object"
17
+ }
18
+ },
19
+ "required": [
20
+ "output",
21
+ "schema"
22
+ ],
23
+ "type": "object",
24
+ "title": "validate_jsonArguments"
25
+ },
26
+ "output_schema": {
27
+ "type": "object",
28
+ "additionalProperties": true,
29
+ "title": "validate_jsonDictOutput"
30
+ }
31
+ }
@@ -0,0 +1,48 @@
1
+ import { BantamError } from './errors.js';
2
+ export declare class AssetNotFound extends BantamError {
3
+ }
4
+ /** The agent-facing view of a tool asset — Python's `bantamkit.client.Tool`. */
5
+ export interface Tool {
6
+ name: string;
7
+ description: string;
8
+ parameters: unknown;
9
+ }
10
+ /**
11
+ * The three arms, in Python's order.
12
+ *
13
+ * 1. `$BANTAMKIT_ASSETS`, verbatim, no existence check — the override wins even when
14
+ * it is wrong, so a wrong override fails naming itself rather than falling through
15
+ * to a pack the operator did not ask for.
16
+ * 2. The packaged pack, resolved off THIS module. When installed from the tarball
17
+ * this module is `<pkg>/dist/assets.js`, so `../assets/` is `<pkg>/assets/`.
18
+ * **This is the arm npx uses.**
19
+ * 3. The repository pack, for a dev checkout, where nothing has been vendored into
20
+ * `runtime-ts/assets/`. This module is `<repo>/runtime-ts/dist/assets.js`, so the
21
+ * repo root is two levels up — the count is proved by `test/assets.test.mjs`,
22
+ * which builds the layout in a temp dir and imports the real build output out of
23
+ * it. Python reaches the same place with `Path(__file__).resolve().parents[3]`
24
+ * from `<repo>/runtime-py/src/bantamkit/assets.py`, and resolves symlinks on this
25
+ * arm only; that asymmetry is mirrored here.
26
+ */
27
+ export declare function assetsRoot(): string;
28
+ /**
29
+ * Return one tool asset VERBATIM — every key, including the ones no surface uses.
30
+ *
31
+ * `loadTool` below narrows the same file to the three fields a model is shown. This
32
+ * returns the whole manifest entry, because a server has to read `surfaces` (may I
33
+ * register this at all?) and `output_schema` (what do I advertise as the return shape?)
34
+ * and neither belongs on the agent-facing `Tool`.
35
+ */
36
+ export declare function loadToolAsset(name: string): Record<string, unknown>;
37
+ /**
38
+ * The agent-facing view of a tool asset: name, description, input schema.
39
+ *
40
+ * The three keys are named EXPLICITLY rather than splatted. `assets/tools/` is shared by
41
+ * two tool surfaces and carries fields that only the MCP one consumes; a loader that
42
+ * passed the dict through would add them to every tool definition an eval-run model is
43
+ * shown, changing the agent surface every time the manifest grows.
44
+ */
45
+ export declare function loadTool(name: string): Tool;
46
+ /** Return a JSON Schema asset (parsed) — e.g. the shift-work checkpoint contract. */
47
+ export declare function loadSchema(name: string): Record<string, unknown>;
48
+ export declare function loadSkill(name: string): string;
package/dist/assets.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Locate and load the language-agnostic asset pack.
3
+ *
4
+ * A port of `runtime-py/src/bantamkit/assets.py`, arm for arm. The Python module is the
5
+ * reference; where this file deviates the deviation is a comment, not an accident.
6
+ */
7
+ import { existsSync, realpathSync } from 'node:fs';
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { BantamError } from './errors.js';
11
+ import { pyReadText } from './memory/pyfs.js';
12
+ export class AssetNotFound extends BantamError {
13
+ }
14
+ /**
15
+ * `Path.read_text(encoding="utf-8")`, which is `memory/pyfs.pyReadText` and NOT a second
16
+ * spelling of it.
17
+ *
18
+ * It WAS a second spelling — `readFileSync(p, 'utf8').replace(/\r\n?/g, '\n')` — and N8
19
+ * measured the two decoders disagreeing: `pyReadText` dropped a leading UTF-8 BOM (its
20
+ * `TextDecoder` defaults `ignoreBOM` to false) while this one kept it, so one package held
21
+ * two UTF-8 decodes with opposite answers on the same three bytes. This one happened to be
22
+ * the one that matched CPython; the fix is not to keep the right copy but to keep ONE, so
23
+ * the next divergence has nowhere to hide. What this arm gains in the trade: `pyReadText`
24
+ * decodes STRICTLY, so a tool asset that is not valid UTF-8 now raises CPython's
25
+ * `UnicodeDecodeError` here instead of reaching a model with U+FFFD substituted into the
26
+ * description it reads. Universal-newline translation is unchanged and lives there too.
27
+ */
28
+ function readTextUniversal(path) {
29
+ return pyReadText(path);
30
+ }
31
+ /**
32
+ * The three arms, in Python's order.
33
+ *
34
+ * 1. `$BANTAMKIT_ASSETS`, verbatim, no existence check — the override wins even when
35
+ * it is wrong, so a wrong override fails naming itself rather than falling through
36
+ * to a pack the operator did not ask for.
37
+ * 2. The packaged pack, resolved off THIS module. When installed from the tarball
38
+ * this module is `<pkg>/dist/assets.js`, so `../assets/` is `<pkg>/assets/`.
39
+ * **This is the arm npx uses.**
40
+ * 3. The repository pack, for a dev checkout, where nothing has been vendored into
41
+ * `runtime-ts/assets/`. This module is `<repo>/runtime-ts/dist/assets.js`, so the
42
+ * repo root is two levels up — the count is proved by `test/assets.test.mjs`,
43
+ * which builds the layout in a temp dir and imports the real build output out of
44
+ * it. Python reaches the same place with `Path(__file__).resolve().parents[3]`
45
+ * from `<repo>/runtime-py/src/bantamkit/assets.py`, and resolves symlinks on this
46
+ * arm only; that asymmetry is mirrored here.
47
+ */
48
+ export function assetsRoot() {
49
+ const env = process.env.BANTAMKIT_ASSETS;
50
+ if (env) {
51
+ return env;
52
+ }
53
+ const hereDir = dirname(fileURLToPath(import.meta.url));
54
+ const packaged = join(hereDir, '..', 'assets');
55
+ if (existsSync(packaged)) {
56
+ return packaged;
57
+ }
58
+ const repo = join(realpathSync(hereDir), '..', '..', 'assets');
59
+ if (existsSync(repo)) {
60
+ return repo;
61
+ }
62
+ throw new AssetNotFound('no assets directory found; set BANTAMKIT_ASSETS');
63
+ }
64
+ /**
65
+ * Return one tool asset VERBATIM — every key, including the ones no surface uses.
66
+ *
67
+ * `loadTool` below narrows the same file to the three fields a model is shown. This
68
+ * returns the whole manifest entry, because a server has to read `surfaces` (may I
69
+ * register this at all?) and `output_schema` (what do I advertise as the return shape?)
70
+ * and neither belongs on the agent-facing `Tool`.
71
+ */
72
+ export function loadToolAsset(name) {
73
+ const path = join(assetsRoot(), 'tools', `${name}.json`);
74
+ if (!existsSync(path)) {
75
+ throw new AssetNotFound(`tool asset not found: ${path}`);
76
+ }
77
+ return JSON.parse(readTextUniversal(path));
78
+ }
79
+ /**
80
+ * The agent-facing view of a tool asset: name, description, input schema.
81
+ *
82
+ * The three keys are named EXPLICITLY rather than splatted. `assets/tools/` is shared by
83
+ * two tool surfaces and carries fields that only the MCP one consumes; a loader that
84
+ * passed the dict through would add them to every tool definition an eval-run model is
85
+ * shown, changing the agent surface every time the manifest grows.
86
+ */
87
+ export function loadTool(name) {
88
+ const data = loadToolAsset(name);
89
+ return {
90
+ name: data['name'],
91
+ description: data['description'],
92
+ parameters: data['parameters'],
93
+ };
94
+ }
95
+ /** Return a JSON Schema asset (parsed) — e.g. the shift-work checkpoint contract. */
96
+ export function loadSchema(name) {
97
+ const path = join(assetsRoot(), 'schemas', `${name}.json`);
98
+ if (!existsSync(path)) {
99
+ throw new AssetNotFound(`schema asset not found: ${path}`);
100
+ }
101
+ return JSON.parse(readTextUniversal(path));
102
+ }
103
+ export function loadSkill(name) {
104
+ const path = join(assetsRoot(), 'skills', `${name}.md`);
105
+ if (!existsSync(path)) {
106
+ throw new AssetNotFound(`skill asset not found: ${path}`);
107
+ }
108
+ return readTextUniversal(path);
109
+ }
@@ -0,0 +1,17 @@
1
+ /** Every module `encodings` ships, as `encodings.search_function` would import it. */
2
+ export declare const CODEC_MODULES: ReadonlySet<string>;
3
+ /** `encodings.aliases.aliases`: normalized name -> module. */
4
+ export declare const CODEC_ALIASES: Readonly<Record<string, string>>;
5
+ /**
6
+ * Module -> the 128 characters bytes 0x80..0xFF decode to (`errors="replace"`), and the
7
+ * 128 for 0x00..0x7F only where they are not ASCII (the EBCDIC pages).
8
+ */
9
+ export declare const SINGLE_BYTE_TABLES: Readonly<Record<string, {
10
+ high: string;
11
+ low?: string;
12
+ }>>;
13
+ /**
14
+ * The CJK modules, each -> what bytes 0x80..0xFF decode to ALONE (U+FFFD: not a
15
+ * character by itself, so a lead byte or a refusal).
16
+ */
17
+ export declare const MULTI_BYTE_SINGLES: Readonly<Record<string, string>>;