functionalscript 0.30.0 → 0.31.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 (181) hide show
  1. package/fs/asn.1/module.f.js +18 -22
  2. package/fs/asn.1/proof.f.d.ts +5 -0
  3. package/fs/asn.1/proof.f.js +38 -2
  4. package/fs/base64/module.f.d.ts +9 -0
  5. package/fs/base64/module.f.js +60 -0
  6. package/fs/base64/proof.f.d.ts +11 -0
  7. package/fs/base64/proof.f.js +117 -0
  8. package/fs/base_n/module.f.d.ts +44 -0
  9. package/fs/base_n/module.f.js +54 -0
  10. package/fs/base_n/proof.f.d.ts +10 -0
  11. package/fs/base_n/proof.f.js +86 -0
  12. package/fs/bnf/data/module.f.d.ts +4 -9
  13. package/fs/bnf/data/module.f.js +4 -3
  14. package/fs/bnf/module.f.d.ts +3 -4
  15. package/fs/bnf/module.f.js +3 -2
  16. package/fs/bnf/proof.f.d.ts +9 -0
  17. package/fs/bnf/proof.f.js +98 -1
  18. package/fs/cas/mcp/module.f.d.ts +40 -0
  19. package/fs/cas/mcp/module.f.js +223 -0
  20. package/fs/cas/mcp/proof.f.d.ts +30 -0
  21. package/fs/cas/mcp/proof.f.js +310 -0
  22. package/fs/cas/module.f.d.ts +9 -3
  23. package/fs/cas/module.f.js +104 -69
  24. package/fs/cas/proof.f.d.ts +2 -0
  25. package/fs/cas/proof.f.js +30 -1
  26. package/fs/cbase32/module.f.d.ts +1 -1
  27. package/fs/cbase32/module.f.js +19 -40
  28. package/fs/cbase32/proof.f.d.ts +1 -0
  29. package/fs/cbase32/proof.f.js +15 -0
  30. package/fs/ci/config/module.f.d.ts +6 -6
  31. package/fs/ci/config/module.f.js +6 -6
  32. package/fs/ci/deno/module.f.js +2 -2
  33. package/fs/ci/module.f.d.ts +5 -0
  34. package/fs/ci/module.f.js +2 -3
  35. package/fs/ci/proof.f.d.ts +1 -0
  36. package/fs/ci/proof.f.js +9 -3
  37. package/fs/cli/module.f.d.ts +3 -1
  38. package/fs/cli/module.f.js +22 -12
  39. package/fs/cli/proof.f.d.ts +5 -0
  40. package/fs/cli/proof.f.js +76 -0
  41. package/fs/crypto/hmac/proof.f.d.ts +1 -0
  42. package/fs/crypto/hmac/proof.f.js +8 -0
  43. package/fs/crypto/secp/proof.f.d.ts +1 -0
  44. package/fs/crypto/secp/proof.f.js +10 -0
  45. package/fs/crypto/sign/module.f.js +1 -4
  46. package/fs/dev/module.f.d.ts +5 -3
  47. package/fs/dev/module.f.js +13 -2
  48. package/fs/dev/package_json/proof.f.d.ts +1 -0
  49. package/fs/dev/package_json/proof.f.js +5 -0
  50. package/fs/dev/proof.f.d.ts +1 -0
  51. package/fs/dev/proof.f.js +12 -1
  52. package/fs/djs/ast/module.f.d.ts +1 -1
  53. package/fs/djs/ast/proof.f.d.ts +5 -0
  54. package/fs/djs/ast/proof.f.js +17 -0
  55. package/fs/djs/module.f.d.ts +1 -1
  56. package/fs/djs/module.f.js +2 -3
  57. package/fs/djs/serializer/module.f.js +24 -11
  58. package/fs/djs/transpiler/module.f.js +3 -4
  59. package/fs/effects/memory/module.f.d.ts +37 -0
  60. package/fs/effects/memory/module.f.js +24 -0
  61. package/fs/effects/memory/proof.f.d.ts +6 -0
  62. package/fs/effects/memory/proof.f.js +64 -0
  63. package/fs/effects/mock/module.f.d.ts +7 -2
  64. package/fs/effects/mock/module.f.js +12 -8
  65. package/fs/effects/module.d.ts +1 -1
  66. package/fs/effects/module.f.d.ts +41 -0
  67. package/fs/effects/module.f.js +25 -0
  68. package/fs/effects/module.js +6 -7
  69. package/fs/effects/node/memory/module.d.ts +20 -0
  70. package/fs/effects/node/memory/module.js +44 -0
  71. package/fs/effects/node/memory/proof.d.ts +10 -0
  72. package/fs/effects/node/memory/proof.js +35 -0
  73. package/fs/effects/node/module.d.ts +1 -1
  74. package/fs/effects/node/module.f.d.ts +54 -8
  75. package/fs/effects/node/module.f.js +55 -4
  76. package/fs/effects/node/module.js +166 -135
  77. package/fs/effects/node/proof.f.d.ts +10 -0
  78. package/fs/effects/node/proof.f.js +88 -21
  79. package/fs/effects/node/virtual/module.f.d.ts +6 -0
  80. package/fs/effects/node/virtual/module.f.js +48 -16
  81. package/fs/effects/node/virtual/proof.f.d.ts +17 -0
  82. package/fs/effects/node/virtual/proof.f.js +79 -0
  83. package/fs/effects/proof.f.d.ts +5 -0
  84. package/fs/effects/proof.f.js +54 -34
  85. package/fs/emergent_testing/module.f.d.ts +6 -1
  86. package/fs/emergent_testing/module.f.js +8 -3
  87. package/fs/emergent_testing/proof.f.d.ts +11 -0
  88. package/fs/emergent_testing/proof.f.js +52 -16
  89. package/fs/fjs/module.f.js +2 -2
  90. package/fs/fjs/proof.f.d.ts +8 -0
  91. package/fs/fjs/proof.f.js +39 -0
  92. package/fs/fsc/module.f.d.ts +5 -0
  93. package/fs/fsc/module.f.js +17 -27
  94. package/fs/fsc/proof.f.d.ts +8 -0
  95. package/fs/fsc/proof.f.js +48 -2
  96. package/fs/fsm/module.f.d.ts +2 -3
  97. package/fs/fsm/module.f.js +1 -1
  98. package/fs/html/module.f.d.ts +2 -3
  99. package/fs/html/module.f.js +7 -6
  100. package/fs/html/proof.f.d.ts +1 -0
  101. package/fs/html/proof.f.js +7 -0
  102. package/fs/js/tokenizer/module.f.js +1 -1
  103. package/fs/js/tokenizer/proof.f.js +7 -0
  104. package/fs/json/module.f.d.ts +9 -2
  105. package/fs/json/module.f.js +9 -7
  106. package/fs/json/parser/module.f.js +4 -6
  107. package/fs/json/parser/proof.f.js +19 -10
  108. package/fs/json/proof.f.d.ts +2 -1
  109. package/fs/json/proof.f.js +33 -6
  110. package/fs/json/rpc/module.f.d.ts +5 -0
  111. package/fs/json/rpc/module.f.js +16 -11
  112. package/fs/json/schema/module.f.d.ts +15 -14
  113. package/fs/json/schema/proof.f.d.ts +3 -0
  114. package/fs/json/schema/proof.f.js +16 -1
  115. package/fs/mcp/module.f.d.ts +103 -5
  116. package/fs/mcp/module.f.js +139 -2
  117. package/fs/mcp/proof.f.d.ts +34 -0
  118. package/fs/mcp/proof.f.js +207 -0
  119. package/fs/mcp/stdio/module.f.d.ts +39 -0
  120. package/fs/mcp/stdio/module.f.js +54 -0
  121. package/fs/mcp/stdio/proof.f.d.ts +10 -0
  122. package/fs/mcp/stdio/proof.f.js +92 -0
  123. package/fs/mime/module.f.d.ts +39 -0
  124. package/fs/mime/module.f.js +78 -0
  125. package/fs/mime/proof.f.d.ts +15 -0
  126. package/fs/mime/proof.f.js +30 -0
  127. package/fs/path/module.f.d.ts +8 -0
  128. package/fs/path/module.f.js +10 -3
  129. package/fs/path/proof.f.d.ts +1 -0
  130. package/fs/path/proof.f.js +40 -2
  131. package/fs/text/sgr/proof.f.js +30 -2
  132. package/fs/text/utf8/module.f.d.ts +7 -0
  133. package/fs/text/utf8/module.f.js +85 -31
  134. package/fs/text/utf8/proof.f.d.ts +1 -0
  135. package/fs/text/utf8/proof.f.js +60 -1
  136. package/fs/types/bigfloat/module.f.d.ts +3 -0
  137. package/fs/types/bigfloat/module.f.js +30 -31
  138. package/fs/types/bigint/module.f.d.ts +24 -2
  139. package/fs/types/bigint/module.f.js +30 -2
  140. package/fs/types/bigint/proof.f.d.ts +7 -0
  141. package/fs/types/bigint/proof.f.js +47 -16
  142. package/fs/types/bit_vec/module.f.d.ts +18 -0
  143. package/fs/types/bit_vec/module.f.js +18 -0
  144. package/fs/types/bit_vec/proof.f.d.ts +1 -0
  145. package/fs/types/bit_vec/proof.f.js +13 -1
  146. package/fs/types/function/operator/proof.f.d.ts +2 -0
  147. package/fs/types/function/operator/proof.f.js +20 -2
  148. package/fs/types/list/module.f.js +1 -4
  149. package/fs/types/list/proof.f.d.ts +1 -1
  150. package/fs/types/list/proof.f.js +14 -6
  151. package/fs/types/map/proof.f.d.ts +1 -0
  152. package/fs/types/map/proof.f.js +10 -1
  153. package/fs/types/nibble_set/module.f.d.ts +13 -0
  154. package/fs/types/nibble_set/module.f.js +16 -0
  155. package/fs/types/object/module.f.d.ts +12 -8
  156. package/fs/types/object/module.f.js +9 -2
  157. package/fs/types/object/proof.f.d.ts +14 -0
  158. package/fs/types/object/proof.f.js +4 -0
  159. package/fs/types/ordered_map/proof.f.d.ts +1 -0
  160. package/fs/types/ordered_map/proof.f.js +39 -79
  161. package/fs/types/phantom/module.f.d.ts +22 -0
  162. package/fs/types/phantom/module.f.js +11 -0
  163. package/fs/types/prime_field/proof.f.d.ts +7 -0
  164. package/fs/types/prime_field/proof.f.js +63 -122
  165. package/fs/types/rtti/common/module.f.d.ts +4 -4
  166. package/fs/types/rtti/common/module.f.js +1 -1
  167. package/fs/types/rtti/module.f.d.ts +2 -3
  168. package/fs/types/rtti/parse/module.f.js +0 -1
  169. package/fs/types/rtti/ts/module.f.d.ts +26 -32
  170. package/fs/types/rtti/ts/module.f.js +2 -2
  171. package/fs/types/rtti/ts/proof.f.js +3 -3
  172. package/fs/types/rtti/validate/module.f.js +0 -1
  173. package/fs/types/sorted_list/module.f.d.ts +6 -0
  174. package/fs/types/sorted_list/module.f.js +11 -0
  175. package/fs/types/sorted_set/module.f.js +2 -8
  176. package/fs/types/ts/module.f.d.ts +1 -1
  177. package/fs/types/ts/module.f.js +3 -1
  178. package/fs/types/ts/proof.f.js +3 -2
  179. package/fs/website/proof.f.d.ts +1 -0
  180. package/fs/website/proof.f.js +8 -1
  181. package/package.json +3 -3
package/fs/bnf/proof.f.js CHANGED
@@ -1,7 +1,104 @@
1
1
  import { classic, deterministic } from "./testlib.f.js";
2
+ import { rangeEncode, str, set, range, commaJoin0Plus, isEmpty, oneEncode, repeat1Plus, } from "./module.f.js";
2
3
  export const proof = {
3
4
  test: () => {
4
5
  classic();
5
6
  deterministic();
6
- }
7
+ },
8
+ throw: {
9
+ rangeEncodeInvalid: [
10
+ () => { rangeEncode(-1, 0); },
11
+ () => { rangeEncode(0, -1); },
12
+ () => { rangeEncode(5, 3); },
13
+ ],
14
+ rangeInvalid: [
15
+ () => { range('a'); },
16
+ () => { range('abc'); },
17
+ ],
18
+ },
19
+ str: [
20
+ () => {
21
+ const result = str('a');
22
+ if (typeof result !== 'number') {
23
+ throw result;
24
+ }
25
+ if (result !== oneEncode(0x61)) {
26
+ throw result;
27
+ }
28
+ },
29
+ () => {
30
+ const result = str('ab');
31
+ if (!Array.isArray(result)) {
32
+ throw result;
33
+ }
34
+ if (result.length !== 2) {
35
+ throw result;
36
+ }
37
+ },
38
+ ],
39
+ set: () => {
40
+ const result = set('abc');
41
+ if (typeof result !== 'object' || result === null) {
42
+ throw result;
43
+ }
44
+ if (!('a' in result)) {
45
+ throw result;
46
+ }
47
+ if (!('b' in result)) {
48
+ throw result;
49
+ }
50
+ if (!('c' in result)) {
51
+ throw result;
52
+ }
53
+ },
54
+ commaJoin0Plus: () => {
55
+ const ws = '';
56
+ const item = 'x';
57
+ const result = commaJoin0Plus(ws)('[]', item);
58
+ if (!Array.isArray(result)) {
59
+ throw result;
60
+ }
61
+ if (result[0] !== '[') {
62
+ throw result;
63
+ }
64
+ },
65
+ isEmpty: [
66
+ () => { if (!isEmpty('')) {
67
+ throw 'empty string should be empty';
68
+ } },
69
+ () => { if (!isEmpty([])) {
70
+ throw 'empty array should be empty';
71
+ } },
72
+ () => { if (isEmpty('a')) {
73
+ throw 'non-empty string should not be empty';
74
+ } },
75
+ () => {
76
+ const f = () => '';
77
+ if (!isEmpty(f)) {
78
+ throw 'function returning empty string should be empty';
79
+ }
80
+ },
81
+ ],
82
+ repeat1Plus: [
83
+ () => {
84
+ const result = repeat1Plus('x');
85
+ if (result[0] !== 'x') {
86
+ throw result[0];
87
+ }
88
+ if (typeof result[1] !== 'function') {
89
+ throw 'expected repeat0Plus function';
90
+ }
91
+ },
92
+ () => {
93
+ const rule = 'ab';
94
+ const result = repeat1Plus(rule);
95
+ if (result[0] !== rule) {
96
+ throw result[0];
97
+ }
98
+ const inner = result[1]();
99
+ if (!('some' in inner) || !('none' in inner)) {
100
+ throw 'expected Option shape';
101
+ }
102
+ },
103
+ ],
7
104
  };
@@ -0,0 +1,40 @@
1
+ import { type Effect, type Operation } from '../../effects/module.f.ts';
2
+ import { type MemOp } from '../../effects/memory/module.f.ts';
3
+ import { type Vec } from '../../types/bit_vec/module.f.ts';
4
+ import { type Read, type ReadFile, type Write } from '../../effects/node/module.f.ts';
5
+ import { type McpConfig, type McpHandlers } from '../../mcp/module.f.ts';
6
+ import type { Cas } from '../module.f.ts';
7
+ /** Arguments for `cas_add`: content to store, with optional encoding type. */
8
+ export declare const casAddArgs: {
9
+ readonly content: import("../../types/rtti/module.f.ts").String;
10
+ readonly type: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Or<[import("../../types/rtti/module.f.ts").Or<["text", "base64"]>, "url"]>, undefined]>;
11
+ };
12
+ /** Arguments for `cas_get`: the cBase32 hash to look up; optionally request inline content. */
13
+ export declare const casGetArgs: {
14
+ readonly hash: import("../../types/rtti/module.f.ts").String;
15
+ readonly content: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Boolean, undefined]>;
16
+ };
17
+ /** Arguments for `cas_list`: none. */
18
+ export declare const casListArgs: {};
19
+ /**
20
+ * MCP handlers for an injected `Cas<O>` — generic in `O` exactly like `Cas`
21
+ * itself, so the same handlers run over `Fs` (production) or memory (tests).
22
+ *
23
+ * When `toUrl` is provided, `cas_get` includes the `url` field pointing to
24
+ * the blob on the local filesystem. When absent (e.g. memory-backed tests),
25
+ * `url` is omitted.
26
+ */
27
+ export declare const casMcpHandlers: <O extends Operation>(c: Cas<O>, toUrl?: (hash: Vec) => string) => McpHandlers<ReadFile | O>;
28
+ /**
29
+ * Static MCP configuration for the CAS server: advertises the `tools`
30
+ * capability, identifies the server, and pins the protocol version.
31
+ */
32
+ export declare const casConfig: McpConfig;
33
+ /**
34
+ * Runs the CAS MCP server over stdio: allocates the session-state slot, builds
35
+ * the `mcpStep` for `c`, and drives the read → parse → dispatch → write loop
36
+ * until stdin EOF. Generic in `O` so it composes with any `Cas<O>` backing.
37
+ *
38
+ * When `toUrl` is provided, `cas_get` includes the blob's filesystem URL.
39
+ */
40
+ export declare const casMcpServer: <O extends Operation>(c: Cas<O>, toUrl?: (hash: Vec) => string) => Effect<Read | Write | MemOp | ReadFile | O, void>;
@@ -0,0 +1,223 @@
1
+ /**
2
+ * MCP adapter for the content-addressable store.
3
+ *
4
+ * Maps `Cas<O>` operations onto MCP tools, so an agent that speaks MCP can
5
+ * store a blob and get back its hash, fetch a blob by hash, and enumerate
6
+ * what is stored — without shelling out to the `cas` CLI. The store itself
7
+ * (`fs/cas/module.f.ts`) stays transport-agnostic; this is an additional
8
+ * front end alongside the CLI `main`.
9
+ *
10
+ * ## Tools
11
+ *
12
+ * | Tool | args | CAS call | result |
13
+ * |------------|-------------------------------|------------------|-------------------------------------|
14
+ * | `cas_add` | `{ content, type? }` | `c.write(value)` | hash (cBase32) |
15
+ * | `cas_get` | `{ hash, content?: boolean }` | `c.read(key)` | JSON `{length,mime_type,type[,content]}` |
16
+ * | `cas_list` | `{}` | `c.list()` | hashes, one per line |
17
+ *
18
+ * ## `cas_add` input encoding
19
+ *
20
+ * The optional `type` field controls how `content` is interpreted:
21
+ * - `'text'` (default, or omitted): `content` is a UTF-8 string stored as raw
22
+ * bytes. Most agent-generated content (scripts, JSON, prompts) can be stored
23
+ * without any encoding step.
24
+ * - `'base64'`: `content` is RFC 4648 base64, decoded to bytes before storage.
25
+ * Use this for pre-encoded binary payloads.
26
+ * - `'url'`: `content` is a filesystem path; the server reads the file at that
27
+ * path and stores its raw bytes.
28
+ *
29
+ * ## `cas_get` output
30
+ *
31
+ * Always returns a JSON object `{ length, mime_type, type[, url][, content] }`.
32
+ * `type` is always present (`'text'` or `'base64'`). When `content: true` is
33
+ * requested, the inline payload is also included. Two-phase MIME detection
34
+ * determines the encoding:
35
+ *
36
+ * 1. **Magic-byte sniffing** (`fs/mime` `detect`): PNG/JPEG/GIF/WebP/PDF/ZIP →
37
+ * `type: 'base64'` with the detected `mime_type`.
38
+ * 2. **UTF-8 validation** (`fs/text/utf8` `fromVec`): valid UTF-8 →
39
+ * `type: 'text'`, `mime_type: 'text/plain'`.
40
+ * 3. **Fallback**: `type: 'base64'`, `mime_type: 'application/octet-stream'`.
41
+ *
42
+ * When `content: false` (default), only `{ length, mime_type, type[, url] }` is
43
+ * returned — no content transfer.
44
+ *
45
+ * ## Encoding split: hashes vs. content
46
+ *
47
+ * `Cas<O>` deals in `Vec` (bit vectors); MCP models only `textContent` today.
48
+ * **Hashes** travel as cBase32 (`fs/cbase32`) — the canonical CAS hash format,
49
+ * shared with the CLI and the on-disk store layout. **Content** encoding is
50
+ * determined at read time as described above.
51
+ *
52
+ * ## Error convention
53
+ *
54
+ * MCP draws a line the dispatcher already respects: *protocol* failures
55
+ * (unknown method, malformed JSON-RPC params) are JSON-RPC errors handled by
56
+ * `mcpStep`. *Tool* failures come back as a normal `tools/call` result with
57
+ * `isError: true` and a text explanation, so the model can read and react:
58
+ * - `type: 'base64'` with malformed content (base64 `decode` → `null`) → `isError`
59
+ * - malformed `hash` (`cBase32ToVec` → `null`) → `isError`
60
+ * - `cas_get` on an absent hash (`c.read` → `undefined`) → `isError`
61
+ * - unknown tool `name` → `isError`
62
+ *
63
+ * @module
64
+ */
65
+ import { string, option, or, boolean } from "../../types/rtti/module.f.js";
66
+ import { validate } from "../../types/rtti/validate/module.f.js";
67
+ import { toJsonSchema } from "../../json/schema/module.f.js";
68
+ import { pure } from "../../effects/module.f.js";
69
+ import { create } from "../../effects/memory/module.f.js";
70
+ import { cBase32ToVec, vecToCBase32 } from "../../cbase32/module.f.js";
71
+ import { decode as base64Decode, encode as base64Encode } from "../../base64/module.f.js";
72
+ import { utf8 } from "../../text/module.f.js";
73
+ import { detect } from "../../mime/module.f.js";
74
+ import { length as bitVecLength } from "../../types/bit_vec/module.f.js";
75
+ import { readFile } from "../../effects/node/module.f.js";
76
+ import { stdioTransport } from "../../mcp/stdio/module.f.js";
77
+ import { mcpStep, uninitializedState, } from "../../mcp/module.f.js";
78
+ import { fromVec } from "../../text/utf8/module.f.js";
79
+ // ── Argument schemas (declared once, used for both inputSchema and validate) ─────
80
+ /** Arguments for `cas_add`: content to store, with optional encoding type. */
81
+ export const casAddArgs = { content: string, type: option(or(or('text', 'base64'), 'url')) };
82
+ /** Arguments for `cas_get`: the cBase32 hash to look up; optionally request inline content. */
83
+ export const casGetArgs = { hash: string, content: option(boolean) };
84
+ /** Arguments for `cas_list`: none. */
85
+ export const casListArgs = {};
86
+ // ── Tool descriptors ────────────────────────────────────────────────────────────
87
+ const casAddTool = {
88
+ name: 'cas_add',
89
+ description: 'Store content and return its hash (cBase32). Pass type:"base64" for binary; type:"url" to read from a filesystem path; omit or pass type:"text" for UTF-8 text (default).',
90
+ inputSchema: toJsonSchema(casAddArgs),
91
+ };
92
+ const casGetTool = {
93
+ name: 'cas_get',
94
+ description: 'Inspect a blob by hash. Always returns JSON {length,mime_type,type[,url]} where type is "text" or "base64". Pass content:true to also include the inline content string.',
95
+ inputSchema: toJsonSchema(casGetArgs),
96
+ };
97
+ const casListTool = {
98
+ name: 'cas_list',
99
+ description: 'List all stored content hashes (cBase32), one per line.',
100
+ inputSchema: toJsonSchema(casListArgs),
101
+ };
102
+ // ── Result helpers ──────────────────────────────────────────────────────────────
103
+ /** A successful single-text-block tool result. */
104
+ const okResult = (text) => ({ content: [{ type: 'text', text }] });
105
+ /** A tool-level failure: in-band `isError` result with a text explanation. */
106
+ const errorResult = (text) => ({ content: [{ type: 'text', text }], isError: true });
107
+ // ── Handlers ────────────────────────────────────────────────────────────────────
108
+ /**
109
+ * MCP handlers for an injected `Cas<O>` — generic in `O` exactly like `Cas`
110
+ * itself, so the same handlers run over `Fs` (production) or memory (tests).
111
+ *
112
+ * When `toUrl` is provided, `cas_get` includes the `url` field pointing to
113
+ * the blob on the local filesystem. When absent (e.g. memory-backed tests),
114
+ * `url` is omitted.
115
+ */
116
+ export const casMcpHandlers = (c, toUrl) => ({
117
+ toolsList: () => pure({ tools: [casAddTool, casGetTool, casListTool] }),
118
+ toolsCall: ({ name, arguments: args }) => {
119
+ const a = args === undefined ? {} : args;
120
+ switch (name) {
121
+ case 'cas_add': {
122
+ const [t, r] = validate(casAddArgs)(a);
123
+ if (t === 'error') {
124
+ return pure(errorResult(`invalid arguments: ${r.message}`));
125
+ }
126
+ const encoding = r.type ?? 'text';
127
+ if (encoding === 'url') {
128
+ return readFile(r.content).step(result => {
129
+ if (result[0] === 'error') {
130
+ return pure(errorResult(`cannot read file: ${r.content}: ${result[1]}`));
131
+ }
132
+ return c.write(result[1]).step(hash => pure(okResult(vecToCBase32(hash))));
133
+ });
134
+ }
135
+ let value;
136
+ if (encoding === 'base64') {
137
+ value = base64Decode(r.content);
138
+ if (value === null) {
139
+ return pure(errorResult(`invalid base64 content: ${r.content}`));
140
+ }
141
+ }
142
+ else {
143
+ value = utf8(r.content);
144
+ }
145
+ return c.write(value).step(hash => pure(okResult(vecToCBase32(hash))));
146
+ }
147
+ case 'cas_get': {
148
+ const [t, r] = validate(casGetArgs)(a);
149
+ if (t === 'error') {
150
+ return pure(errorResult(`invalid arguments: ${r.message}`));
151
+ }
152
+ const key = cBase32ToVec(r.hash);
153
+ if (key === null) {
154
+ return pure(errorResult(`invalid cBase32 hash: ${r.hash}`));
155
+ }
156
+ return c.read(key).step(value => {
157
+ if (value === undefined) {
158
+ return pure(errorResult(`no such hash: ${r.hash}`));
159
+ }
160
+ const byteLength = Number(bitVecLength(value) / 8n);
161
+ // Phase 1: magic-byte sniffing for known binary formats.
162
+ const detectedMime = detect(value);
163
+ if (detectedMime !== null) {
164
+ const url = toUrl?.(key);
165
+ const meta = { length: byteLength, mime_type: detectedMime, type: 'base64', ...(url !== undefined && { url }) };
166
+ if (r.content === true) {
167
+ const blob = base64Encode(value);
168
+ if (blob === null) {
169
+ return pure(errorResult(`content is not byte-aligned: ${r.hash}`));
170
+ }
171
+ return pure(okResult(JSON.stringify({ ...meta, content: blob })));
172
+ }
173
+ return pure(okResult(JSON.stringify(meta)));
174
+ }
175
+ // Phase 2: UTF-8 validation — text if valid, octet-stream otherwise.
176
+ const str = fromVec(value);
177
+ const url = toUrl?.(key);
178
+ if (str !== null) {
179
+ const meta = { length: byteLength, mime_type: 'text/plain', type: 'text', ...(url !== undefined && { url }) };
180
+ if (r.content === true) {
181
+ return pure(okResult(JSON.stringify({ ...meta, content: str })));
182
+ }
183
+ return pure(okResult(JSON.stringify(meta)));
184
+ }
185
+ const meta = { length: byteLength, mime_type: 'application/octet-stream', type: 'base64', ...(url !== undefined && { url }) };
186
+ if (r.content === true) {
187
+ const blob = base64Encode(value);
188
+ if (blob === null) {
189
+ return pure(errorResult(`content is not byte-aligned: ${r.hash}`));
190
+ }
191
+ return pure(okResult(JSON.stringify({ ...meta, content: blob })));
192
+ }
193
+ return pure(okResult(JSON.stringify(meta)));
194
+ });
195
+ }
196
+ case 'cas_list': {
197
+ return c.list().step(hashes => pure(okResult(hashes.map(vecToCBase32).join('\n'))));
198
+ }
199
+ default: {
200
+ return pure(errorResult(`unknown tool: ${name}`));
201
+ }
202
+ }
203
+ },
204
+ });
205
+ // ── Session configuration ───────────────────────────────────────────────────────
206
+ /**
207
+ * Static MCP configuration for the CAS server: advertises the `tools`
208
+ * capability, identifies the server, and pins the protocol version.
209
+ */
210
+ export const casConfig = {
211
+ serverInfo: { name: 'functionalscript-cas', version: '0.30.0' },
212
+ capabilities: { tools: {} },
213
+ protocolVersion: '2024-11-05',
214
+ };
215
+ // ── Server ──────────────────────────────────────────────────────────────────────
216
+ /**
217
+ * Runs the CAS MCP server over stdio: allocates the session-state slot, builds
218
+ * the `mcpStep` for `c`, and drives the read → parse → dispatch → write loop
219
+ * until stdin EOF. Generic in `O` so it composes with any `Cas<O>` backing.
220
+ *
221
+ * When `toUrl` is provided, `cas_get` includes the blob's filesystem URL.
222
+ */
223
+ export const casMcpServer = (c, toUrl) => create(uninitializedState).step(key => stdioTransport(mcpStep(casConfig)(casMcpHandlers(c, toUrl))(key)));
@@ -0,0 +1,30 @@
1
+ export declare const proof: {
2
+ toolsListAdvertisesThreeTools: () => void;
3
+ addReturnsHash: () => void;
4
+ textAddGetMetaRoundTrips: () => void;
5
+ textAddGetContentRoundTrips: () => void;
6
+ binaryAddGetMetaRoundTrips: () => void;
7
+ binaryAddGetContentRoundTrips: () => void;
8
+ addGetRoundTrips: () => void;
9
+ getUntypedReturnsText: () => void;
10
+ getTypedReturnsBinaryJson: () => void;
11
+ listEnumeratesStoredHashes: () => void;
12
+ addInvalidContentIsError: () => void;
13
+ addBadLengthContentIsError: () => void;
14
+ getUnterminatedHashIsError: () => void;
15
+ addMissingContentIsError: () => void;
16
+ getMissingHashArgumentIsError: () => void;
17
+ getInvalidHashIsError: () => void;
18
+ getMissingHashIsError: () => void;
19
+ unknownToolIsError: () => void;
20
+ toolErrorIsNotJsonRpcError: () => void;
21
+ addUrlStoresFileAndReturnsHash: () => void;
22
+ addUrlRoundTrips: () => void;
23
+ addUrlMissingFileIsError: () => void;
24
+ getMetaReturnsLengthAndMimeType: () => void;
25
+ getMetaBinaryBlob: () => void;
26
+ getMetaOctetStreamForUnknownBinary: () => void;
27
+ getMetaNoUrlWhenToUrlAbsent: () => void;
28
+ getMetaMissingHashIsError: () => void;
29
+ getMetaInvalidHashIsError: () => void;
30
+ };