functionalscript 0.46.0 → 0.47.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/fjs/basen/cbase32/module.f.mjs +18 -10
- package/fjs/basen/cbase32/proof.f.d.mts +3 -0
- package/fjs/basen/cbase32/proof.f.mjs +24 -2
- package/fjs/basen/proof.f.mjs +8 -16
- package/fjs/bnf/ll1/proof.f.d.mts +2 -0
- package/fjs/bnf/ll1/proof.f.mjs +33 -18
- package/fjs/bnf/module.f.d.mts +2 -2
- package/fjs/bnf/module.f.mjs +2 -2
- package/fjs/cas/proof.f.mjs +4 -26
- package/fjs/ci/common/module.f.mjs +6 -0
- package/fjs/ci/config/module.f.d.mts +7 -7
- package/fjs/ci/config/module.f.mjs +7 -7
- package/fjs/cli/module.f.mjs +14 -10
- package/fjs/crypto/vdf/module.f.d.mts +0 -1
- package/fjs/crypto/vdf/module.f.mjs +3 -14
- package/fjs/dev/module.f.mjs +10 -20
- package/fjs/djs/parser/module.f.d.mts +101 -53
- package/fjs/djs/parser/module.f.mjs +732 -525
- package/fjs/djs/parser/proof.f.d.mts +6 -0
- package/fjs/djs/parser/proof.f.mjs +315 -11
- package/fjs/djs/parser/types.d.ts +29 -2
- package/fjs/djs/tokenizer/module.f.mjs +122 -105
- package/fjs/djs/tokenizer/proof.f.mjs +17 -0
- package/fjs/djs/transpiler/module.f.mjs +4 -5
- package/fjs/djs/transpiler/proof.f.d.mts +2 -0
- package/fjs/djs/transpiler/proof.f.mjs +25 -0
- package/fjs/edag/amnesia/module.f.d.mts +20 -0
- package/fjs/edag/amnesia/module.f.mjs +303 -0
- package/fjs/edag/amnesia/proof.f.d.mts +79 -0
- package/fjs/edag/amnesia/proof.f.mjs +573 -0
- package/fjs/edag/amnesia/types.d.ts +15 -0
- package/fjs/edag/module.f.d.mts +410 -0
- package/fjs/edag/module.f.mjs +559 -0
- package/fjs/edag/proof.f.d.mts +116 -0
- package/fjs/edag/proof.f.mjs +686 -0
- package/fjs/edag/types.d.ts +55 -0
- package/fjs/effects/node/memory/module.d.mts +37 -3
- package/fjs/effects/node/memory/module.mjs +38 -4
- package/fjs/effects/node/memory/proof.d.mts +5 -2
- package/fjs/effects/node/memory/proof.mjs +36 -6
- package/fjs/effects/node/module.d.mts +9 -1
- package/fjs/effects/node/module.f.d.mts +39 -0
- package/fjs/effects/node/module.f.mjs +46 -0
- package/fjs/effects/node/module.mjs +234 -32
- package/fjs/effects/node/types.d.ts +28 -2
- package/fjs/effects/node/virtual/module.f.d.mts +19 -10
- package/fjs/effects/node/virtual/module.f.mjs +246 -32
- package/fjs/effects/node/virtual/proof.f.d.mts +17 -2
- package/fjs/effects/node/virtual/proof.f.mjs +232 -8
- package/fjs/effects/node/virtual/types.d.ts +57 -1
- package/fjs/emergent_testing/browser/proof.d.mts +55 -0
- package/fjs/emergent_testing/browser/proof.mjs +352 -0
- package/fjs/emergent_testing/browser/species.proof.d.mts +4 -0
- package/fjs/emergent_testing/browser/species.proof.mjs +45 -0
- package/fjs/emergent_testing/browser.d.mts +79 -0
- package/fjs/emergent_testing/browser.mjs +432 -0
- package/fjs/emergent_testing/proof.f.d.mts +1 -1
- package/fjs/emergent_testing/proof.f.mjs +2 -2
- package/fjs/js/string_escape/module.f.d.mts +47 -0
- package/fjs/js/string_escape/module.f.mjs +95 -0
- package/fjs/js/string_escape/proof.f.d.mts +8 -0
- package/fjs/js/string_escape/proof.f.mjs +68 -0
- package/fjs/js/tokenizer/module.f.mjs +14 -12
- package/fjs/mcp/cas/module.f.d.mts +1 -7
- package/fjs/mcp/cas/module.f.mjs +24 -15
- package/fjs/mcp/evo/module.f.mjs +9 -24
- package/fjs/mcp/module.f.d.mts +3 -1
- package/fjs/mcp/module.f.mjs +4 -2
- package/fjs/media/json/rtti/module.f.d.mts +2 -2
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/schema/module.f.d.mts +15 -5
- package/fjs/media/json/schema/module.f.mjs +42 -15
- package/fjs/media/json/schema/proof.f.d.mts +10 -1
- package/fjs/media/json/schema/proof.f.mjs +88 -16
- package/fjs/media/json/serializer/module.f.mjs +14 -26
- package/fjs/media/json/types.d.ts +1 -1
- package/fjs/media/lock/module.f.d.mts +8 -3
- package/fjs/media/lock/module.f.mjs +9 -3
- package/fjs/media/lock/proof.f.d.mts +1 -1
- package/fjs/media/lock/proof.f.mjs +2 -2
- package/fjs/media/lock/types.d.ts +1 -3
- package/fjs/media/module.f.d.mts +20 -13
- package/fjs/media/module.f.mjs +29 -16
- package/fjs/media/note/module.f.d.mts +14 -7
- package/fjs/media/note/module.f.mjs +14 -7
- package/fjs/media/note/proof.f.mjs +1 -1
- package/fjs/media/proof.f.mjs +5 -5
- package/fjs/media/revision/module.f.d.mts +8 -2
- package/fjs/media/revision/module.f.mjs +9 -3
- package/fjs/media/revision/proof.f.mjs +1 -1
- package/fjs/media/revision/types.d.ts +1 -1
- package/fjs/media/type/module.f.d.mts +20 -0
- package/fjs/media/type/module.f.mjs +74 -0
- package/fjs/media/type/proof.f.d.mts +5 -0
- package/fjs/media/type/proof.f.mjs +24 -1
- package/fjs/module.f.mjs +6 -0
- package/fjs/path/module.f.d.mts +60 -3
- package/fjs/path/module.f.mjs +138 -14
- package/fjs/path/proof.f.d.mts +3 -0
- package/fjs/path/proof.f.mjs +298 -2
- package/fjs/protocol/json_rpc/module.f.d.mts +65 -28
- package/fjs/protocol/json_rpc/module.f.mjs +49 -13
- package/fjs/protocol/json_rpc/proof.f.d.mts +4 -0
- package/fjs/protocol/json_rpc/proof.f.mjs +29 -0
- package/fjs/protocol/json_rpc/types.d.ts +12 -2
- package/fjs/protocol/mcp/module.f.d.mts +89 -54
- package/fjs/protocol/mcp/module.f.mjs +110 -56
- package/fjs/protocol/mcp/proof.f.d.mts +13 -0
- package/fjs/protocol/mcp/proof.f.mjs +107 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +3 -4
- package/fjs/protocol/mcp/stdio/proof.f.mjs +2 -2
- package/fjs/protocol/mcp/types.d.ts +14 -2
- package/fjs/text/utf8/module.f.mjs +27 -1
- package/fjs/text/utf8/proof.f.mjs +37 -0
- package/fjs/types/array/module.f.mjs +18 -11
- package/fjs/types/array/proof.f.mjs +27 -1
- package/fjs/types/bigfloat/module.f.d.mts +48 -9
- package/fjs/types/bigfloat/module.f.mjs +147 -59
- package/fjs/types/bigfloat/proof.f.d.mts +2 -0
- package/fjs/types/bigfloat/proof.f.mjs +136 -4
- package/fjs/types/bigfloat/types.d.ts +17 -0
- package/fjs/types/function/module.f.d.mts +11 -2
- package/fjs/types/function/module.f.mjs +20 -2
- package/fjs/types/function/proof.f.mjs +5 -1
- package/fjs/types/phantom/types.d.ts +21 -0
- package/fjs/types/result/module.f.d.mts +4 -4
- package/fjs/types/result/module.f.mjs +2 -2
- package/fjs/types/result/proof.f.d.mts +6 -2
- package/fjs/types/result/proof.f.mjs +17 -1
- package/fjs/types/rtti/common/module.f.d.mts +78 -5
- package/fjs/types/rtti/common/module.f.mjs +168 -3
- package/fjs/types/rtti/common/proof.f.d.mts +14 -0
- package/fjs/types/rtti/common/proof.f.mjs +61 -2
- package/fjs/types/rtti/common/types.d.ts +23 -1
- package/fjs/types/rtti/data/module.f.d.mts +57 -1
- package/fjs/types/rtti/data/module.f.mjs +241 -47
- package/fjs/types/rtti/data/proof.f.d.mts +10 -0
- package/fjs/types/rtti/data/proof.f.mjs +272 -39
- package/fjs/types/rtti/data/types.d.ts +14 -7
- package/fjs/types/rtti/host.proof.d.mts +33 -0
- package/fjs/types/rtti/host.proof.mjs +170 -0
- package/fjs/types/rtti/module.f.d.mts +51 -6
- package/fjs/types/rtti/module.f.mjs +50 -3
- package/fjs/types/rtti/parse/module.f.d.mts +31 -24
- package/fjs/types/rtti/parse/module.f.mjs +197 -59
- package/fjs/types/rtti/parse/proof.f.d.mts +37 -2
- package/fjs/types/rtti/parse/proof.f.mjs +171 -47
- package/fjs/types/rtti/proof.f.d.mts +6 -0
- package/fjs/types/rtti/proof.f.mjs +42 -1
- package/fjs/types/rtti/ts/module.f.d.mts +9 -3
- package/fjs/types/rtti/ts/module.f.mjs +68 -15
- package/fjs/types/rtti/ts/proof.f.d.mts +42 -2
- package/fjs/types/rtti/ts/proof.f.mjs +139 -6
- package/fjs/types/rtti/ts/types.d.ts +164 -16
- package/fjs/types/rtti/types.d.ts +31 -3
- package/fjs/types/rtti/validate/module.f.d.mts +120 -0
- package/fjs/types/rtti/validate/module.f.mjs +351 -0
- package/fjs/types/rtti/validate/proof.f.d.mts +154 -0
- package/fjs/types/rtti/validate/proof.f.mjs +781 -0
- package/fjs/types/ts/types.d.ts +4 -0
- package/fjs/web/module.f.d.mts +115 -0
- package/fjs/web/module.f.mjs +581 -0
- package/fjs/web/proof.f.d.mts +39 -0
- package/fjs/web/proof.f.mjs +413 -0
- package/fjs/web/types.d.ts +39 -0
- package/fjs/website/browser-prepare.d.mts +5 -0
- package/fjs/website/browser-prepare.mjs +115 -0
- package/fjs/website/browser-source.d.mts +37 -0
- package/fjs/website/browser-source.mjs +202 -0
- package/fjs/website/browser-source.proof.d.mts +31 -0
- package/fjs/website/browser-source.proof.mjs +162 -0
- package/fjs/website/browser.d.mts +5 -0
- package/fjs/website/browser.mjs +8 -0
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +62 -9
- package/fjs/website/proof.f.d.mts +4 -0
- package/fjs/website/proof.f.mjs +18 -3
- package/package.json +2 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Unknown } from '../../media/json/types.ts'
|
|
3
|
+
* @import { Assert } from '../../asserts/types.ts'
|
|
4
|
+
* @import { Equal } from '../../types/ts/types.ts'
|
|
3
5
|
* @import { Operation } from '../../effects/types.ts'
|
|
4
6
|
* @import { Effect, NotImplemented } from '../../effects/types.ts'
|
|
5
7
|
* @import { MemOperationMap } from '../../effects/mock/types.ts'
|
|
@@ -7,6 +9,7 @@
|
|
|
7
9
|
* @import {
|
|
8
10
|
* ToolsListParams,
|
|
9
11
|
* ToolsCallParams,
|
|
12
|
+
* ToolsCallResult,
|
|
10
13
|
* McpHandlers,
|
|
11
14
|
* McpConfig,
|
|
12
15
|
* McpSessionState,
|
|
@@ -48,6 +51,11 @@ export declare const proof: {
|
|
|
48
51
|
methodDuringInitializingReturnsNotInitialized: () => void;
|
|
49
52
|
invalidEnvelopeReturnsInvalidRequest: () => void;
|
|
50
53
|
};
|
|
54
|
+
versionNegotiation: {
|
|
55
|
+
supportedRequestIsEchoed: () => void;
|
|
56
|
+
unsupportedRequestGetsLatestSupported: () => void;
|
|
57
|
+
oneVersionCounterProposesItsOnlyVersion: () => void;
|
|
58
|
+
};
|
|
51
59
|
tools: {
|
|
52
60
|
toolsListSucceeds: () => void;
|
|
53
61
|
toolsListPassesCursorToHandler: () => void;
|
|
@@ -62,5 +70,10 @@ export declare const proof: {
|
|
|
62
70
|
};
|
|
63
71
|
registry: {
|
|
64
72
|
toolsCallAbsentArgumentsDefaultsToEmptyObject: () => void;
|
|
73
|
+
inlineSchemaLiteralKeepsItsLiteralMembers: () => void;
|
|
74
|
+
};
|
|
75
|
+
toolResultStep: {
|
|
76
|
+
okIsRendered: () => void;
|
|
77
|
+
errorIsRenderedInBand: () => void;
|
|
65
78
|
};
|
|
66
79
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { Unknown } from '../../media/json/types.ts'
|
|
3
|
+
* @import { Assert } from '../../asserts/types.ts'
|
|
4
|
+
* @import { Equal } from '../../types/ts/types.ts'
|
|
3
5
|
* @import { Operation } from '../../effects/types.ts'
|
|
4
6
|
* @import { Effect, NotImplemented } from '../../effects/types.ts'
|
|
5
7
|
* @import { MemOperationMap } from '../../effects/mock/types.ts'
|
|
@@ -7,6 +9,7 @@
|
|
|
7
9
|
* @import {
|
|
8
10
|
* ToolsListParams,
|
|
9
11
|
* ToolsCallParams,
|
|
12
|
+
* ToolsCallResult,
|
|
10
13
|
* McpHandlers,
|
|
11
14
|
* McpConfig,
|
|
12
15
|
* McpSessionState,
|
|
@@ -15,13 +18,15 @@
|
|
|
15
18
|
|
|
16
19
|
import { assert, assertEq } from '../../asserts/module.f.mjs'
|
|
17
20
|
import { runPure } from '../../effects/module.f.mjs'
|
|
18
|
-
import { history, historyStep, mapStep, pureOk, step } from '../../effects/module.f.mjs'
|
|
21
|
+
import { history, historyStep, mapStep, pureError, pureOk, step } from '../../effects/module.f.mjs'
|
|
19
22
|
import { error, ok, unwrap as unwrapResult } from '../../types/result/module.f.mjs'
|
|
20
23
|
import { run } from '../../effects/mock/module.f.mjs'
|
|
21
24
|
import { internalError } from '../json_rpc/module.f.mjs'
|
|
25
|
+
import { string } from '../../types/rtti/module.f.mjs'
|
|
22
26
|
import { asBase, asNominal, create, read } from '../../effects/memory/module.f.mjs'
|
|
23
27
|
import {
|
|
24
28
|
uninitializedState, mcpStep, notInitialized, fromRegistry, toolEntry, okResult,
|
|
29
|
+
toolResultStep,
|
|
25
30
|
} from './module.f.mjs'
|
|
26
31
|
|
|
27
32
|
// ── Memory mock ────────────────────────────────────────────────────────────────
|
|
@@ -55,11 +60,16 @@ const mock = {
|
|
|
55
60
|
const config = {
|
|
56
61
|
serverInfo: { name: 'test-server', version: '0.1.0' },
|
|
57
62
|
capabilities: { tools: {} },
|
|
58
|
-
|
|
63
|
+
protocolVersions: ['2024-11-05'],
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
const configNoTools = { ...config, capabilities: {} }
|
|
62
67
|
|
|
68
|
+
// A server speaking two revisions, latest first — the case a single
|
|
69
|
+
// `protocolVersion` string could not describe.
|
|
70
|
+
/** @type {McpConfig} */
|
|
71
|
+
const configTwoVersions = { ...config, protocolVersions: ['2025-06-18', '2024-11-05'] }
|
|
72
|
+
|
|
63
73
|
/** @typedef {never} _Op */
|
|
64
74
|
/** @type {McpHandlers<_Op>} */
|
|
65
75
|
const handlers = {
|
|
@@ -208,16 +218,39 @@ const firstText = resp => {
|
|
|
208
218
|
return text
|
|
209
219
|
}
|
|
210
220
|
|
|
221
|
+
// ── `toolResultStep` renderers ────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
// Shared by both branches so each renderer is written once and reached once:
|
|
224
|
+
// the `ok` test exercises `dashes`, the `error` test `errorText`.
|
|
225
|
+
|
|
226
|
+
/** @type {(xs: readonly string[]) => string} */
|
|
227
|
+
const dashes = xs => xs.join('-')
|
|
228
|
+
|
|
229
|
+
/** @type {(e: string) => string} */
|
|
230
|
+
const errorText = e => `failed: ${e}`
|
|
231
|
+
|
|
232
|
+
/** The `text` of the first content item of a `ToolsCallResult`. */
|
|
233
|
+
/** @type {(r: ToolsCallResult) => string} */
|
|
234
|
+
const textOf = r => {
|
|
235
|
+
const [item] = r.content
|
|
236
|
+
assert(item.type === 'text', item)
|
|
237
|
+
return item.text
|
|
238
|
+
}
|
|
239
|
+
|
|
211
240
|
// ── Test messages ─────────────────────────────────────────────────────────────
|
|
212
241
|
|
|
213
|
-
|
|
214
|
-
|
|
242
|
+
/** An `initialize` request asking for `protocolVersion`. */
|
|
243
|
+
/** @type {(protocolVersion: string) => Unknown} */
|
|
244
|
+
const initMsgFor = protocolVersion => ({ jsonrpc: '2.0', method: 'initialize', id: 1,
|
|
245
|
+
params: { protocolVersion, capabilities: {}, clientInfo: { name: 'client', version: '0.0.1' } } })
|
|
246
|
+
|
|
247
|
+
const initMsg = initMsgFor('2024-11-05')
|
|
215
248
|
|
|
216
249
|
const initNotif = { jsonrpc: '2.0', method: 'notifications/initialized' }
|
|
217
250
|
|
|
218
251
|
/** A memory handler that answers as a runner with no such operation. */
|
|
219
252
|
const memNotImplemented = () => (/** @type {_MemoryState} */ state) =>
|
|
220
|
-
/** @type {const} */ ([state, error(
|
|
253
|
+
/** @type {const} */ ([state, error(['notImplemented', 'memRead'])])
|
|
221
254
|
|
|
222
255
|
// Runs one step against a memory mock with `overrides` applied, from a session
|
|
223
256
|
// slot created before them so the slot itself always exists.
|
|
@@ -376,6 +409,35 @@ export const proof = {
|
|
|
376
409
|
},
|
|
377
410
|
},
|
|
378
411
|
|
|
412
|
+
// Version negotiation, per the lifecycle spec: answer with the requested
|
|
413
|
+
// revision when the server speaks it, and with the latest one it does speak
|
|
414
|
+
// otherwise. The counter-proposal is a success frame, so what distinguishes
|
|
415
|
+
// the two branches is the answered version, never the session state.
|
|
416
|
+
versionNegotiation: {
|
|
417
|
+
supportedRequestIsEchoed: () => {
|
|
418
|
+
// `2024-11-05` is supported but not the latest — the case a
|
|
419
|
+
// single-string config had to counter-propose out of.
|
|
420
|
+
const [resp] = step1(configTwoVersions)(initMsgFor('2024-11-05'))
|
|
421
|
+
assertEq(protocolVersion(resp), '2024-11-05')
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
unsupportedRequestGetsLatestSupported: () => {
|
|
425
|
+
const [resp, newState] = step1(configTwoVersions)(initMsgFor('2030-01-01'))
|
|
426
|
+
assertEq(protocolVersion(resp), '2025-06-18')
|
|
427
|
+
assert(newState[0] === 'initializing')
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
// A one-element list answers exactly as the `protocolVersion` string
|
|
431
|
+
// field did: its only revision, whichever branch is taken. The echo
|
|
432
|
+
// half is `lifecycle.initializeReturnsResult`, which asks `config` for
|
|
433
|
+
// the one version it has; this is the counter-proposal half.
|
|
434
|
+
oneVersionCounterProposesItsOnlyVersion: () => {
|
|
435
|
+
const [resp, newState] = step1(config)(initMsgFor('2030-01-01'))
|
|
436
|
+
assertEq(protocolVersion(resp), '2024-11-05')
|
|
437
|
+
assert(newState[0] === 'initializing')
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
|
|
379
441
|
tools: {
|
|
380
442
|
toolsListSucceeds: () => {
|
|
381
443
|
const msg = { jsonrpc: '2.0', method: 'tools/list', id: 5 }
|
|
@@ -453,8 +515,7 @@ export const proof = {
|
|
|
453
515
|
// validating it against the empty-object schema below would fail and
|
|
454
516
|
// this would observe an error result instead of `ok`.
|
|
455
517
|
toolsCallAbsentArgumentsDefaultsToEmptyObject: () => {
|
|
456
|
-
const
|
|
457
|
-
const entry = toolEntry('echo', 'echoes', echoArgs,
|
|
518
|
+
const entry = toolEntry('echo', 'echoes', {},
|
|
458
519
|
() => pureOk(okResult('ok')))
|
|
459
520
|
const handlers = fromRegistry([entry])
|
|
460
521
|
const [r] = runPure(handlers.toolsCall({ name: 'echo' }))
|
|
@@ -463,5 +524,44 @@ export const proof = {
|
|
|
463
524
|
assert(item.type === 'text', item)
|
|
464
525
|
assertEq(item.text, 'ok')
|
|
465
526
|
},
|
|
527
|
+
// The type guarantee `./README.md` documents: an inline schema literal
|
|
528
|
+
// reaches the handler as `Ts<T>`, with no cast on either side. That
|
|
529
|
+
// rests on `toolEntry`'s `const` type parameter — without it `kind`
|
|
530
|
+
// widens to `string` and the assertion below fails, which is the only
|
|
531
|
+
// place that would notice.
|
|
532
|
+
inlineSchemaLiteralKeepsItsLiteralMembers: () => {
|
|
533
|
+
const entry = toolEntry('tag', 'tags', { kind: 'add', name: string },
|
|
534
|
+
({ kind, name }) => {
|
|
535
|
+
/** @typedef {Assert<Equal<typeof kind, 'add'>>} _ConstParameter */
|
|
536
|
+
return pureOk(okResult(`${kind}:${name}`))
|
|
537
|
+
})
|
|
538
|
+
const handlers = fromRegistry([entry])
|
|
539
|
+
const [r] = runPure(handlers.toolsCall(
|
|
540
|
+
{ name: 'tag', arguments: { kind: 'add', name: 'x' } }))
|
|
541
|
+
assert(r !== undefined && r[0] === 'ok', r)
|
|
542
|
+
const [item] = r[1].content
|
|
543
|
+
assert(item.type === 'text', item)
|
|
544
|
+
assertEq(item.text, 'add:x')
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
// `toolResultStep` picks between `okResult` and `errorResult`, so both
|
|
549
|
+
// branches are the whole of it — and the error branch is the one that
|
|
550
|
+
// proves the failure stays in-band (`isError`) instead of escaping as a
|
|
551
|
+
// transport error, which the `never` error channel is the type-level claim
|
|
552
|
+
// of.
|
|
553
|
+
toolResultStep: {
|
|
554
|
+
okIsRendered: () => {
|
|
555
|
+
const [r] = runPure(toolResultStep(pureOk(['a', 'b']), dashes, errorText))
|
|
556
|
+
assert(r !== undefined && r[0] === 'ok', r)
|
|
557
|
+
assertEq(r[1].isError, undefined)
|
|
558
|
+
assertEq(textOf(r[1]), 'a-b')
|
|
559
|
+
},
|
|
560
|
+
errorIsRenderedInBand: () => {
|
|
561
|
+
const [r] = runPure(toolResultStep(pureError('gone'), dashes, errorText))
|
|
562
|
+
assert(r !== undefined && r[0] === 'ok', r)
|
|
563
|
+
assertEq(r[1].isError, true)
|
|
564
|
+
assertEq(textOf(r[1]), 'failed: gone')
|
|
565
|
+
},
|
|
466
566
|
},
|
|
467
567
|
}
|
|
@@ -41,19 +41,18 @@ import { ioError, readLine, write } from '../../../effects/node/module.f.mjs'
|
|
|
41
41
|
import { tryUtf8 } from '../../../text/module.f.mjs'
|
|
42
42
|
import { parse, stringify } from '../../../media/json/module.f.mjs'
|
|
43
43
|
import { sort } from '../../../types/object/module.f.mjs'
|
|
44
|
-
import {
|
|
44
|
+
import { errorResponseOf, internalError, parseError } from '../../json_rpc/module.f.mjs'
|
|
45
45
|
import { error } from '../../../types/result/module.f.mjs'
|
|
46
46
|
|
|
47
47
|
const stringifyJson = stringify(sort)
|
|
48
48
|
|
|
49
49
|
/** The parse-error response (`-32700`, `id: null`) for a malformed input line. */
|
|
50
|
-
|
|
51
|
-
const parseErrorResponse = { jsonrpc, error: parseError, id: null }
|
|
50
|
+
const parseErrorResponse = errorResponseOf(null)(parseError)
|
|
52
51
|
|
|
53
52
|
/** An internal-error response (`-32603`) carrying `id`.
|
|
54
53
|
* @type {(id: Response['id']) => Response}
|
|
55
54
|
*/
|
|
56
|
-
const internalErrorResponse = id => (
|
|
55
|
+
const internalErrorResponse = id => errorResponseOf(id)(internalError)
|
|
57
56
|
|
|
58
57
|
/** Encodes a response as a newline-terminated UTF-8 line and writes it to `stdout`.
|
|
59
58
|
* @type {(resp: Response) => Effect<Write, void, IoChannel>}
|
|
@@ -79,7 +79,7 @@ export const proof = {
|
|
|
79
79
|
// `eofImmediately` above pins it as the clean shutdown it should be.
|
|
80
80
|
readFailurePropagates: () => {
|
|
81
81
|
const runner = mockRun(/** @type {Parameters<typeof mockRun<Read | Write, undefined>>[0]} */ ({
|
|
82
|
-
read: () => (/** @type {undefined} */ s) => [s, error(
|
|
82
|
+
read: () => (/** @type {undefined} */ s) => [s, error(['notImplemented', 'read'])],
|
|
83
83
|
write: () => (/** @type {undefined} */ s) => [s, ok(undefined)],
|
|
84
84
|
}))
|
|
85
85
|
const [, result] = runner(undefined)(stdioTransport(echoStep))
|
|
@@ -95,7 +95,7 @@ export const proof = {
|
|
|
95
95
|
const runner = mockRun(/** @type {Parameters<typeof mockRun<Read | Write, readonly number[]>>[0]} */ ({
|
|
96
96
|
read: () => (/** @type {readonly number[]} */ s) =>
|
|
97
97
|
s.length === 0
|
|
98
|
-
? [s, error(
|
|
98
|
+
? [s, error(['notImplemented', 'read'])]
|
|
99
99
|
: [s.slice(1), ok(s[0])],
|
|
100
100
|
write: () => (/** @type {readonly number[]} */ s) => [s, ok(undefined)],
|
|
101
101
|
}))
|
|
@@ -48,7 +48,7 @@ export type ToolEntry<O extends Operation> = {
|
|
|
48
48
|
* **`never` is a claim, not an absence.** The handler behind this does perform
|
|
49
49
|
* effects and they can fail — a session-state read is dispatched by a runner
|
|
50
50
|
* that may decline it. It says `never` because it has *absorbed* those: a
|
|
51
|
-
* request's failure becomes `
|
|
51
|
+
* request's failure becomes `errorResponseOf(id)(internalError)` and a
|
|
52
52
|
* notification's is dropped, there being no frame to put it in. Spelling that
|
|
53
53
|
* as `Effect<…, never>` puts the decision in the type where a reader can
|
|
54
54
|
* disagree with it, which an opaque payload could not.
|
|
@@ -62,9 +62,21 @@ export type Initializing = readonly ['initializing'];
|
|
|
62
62
|
export type InitializedState = true;
|
|
63
63
|
/** The three phases of an MCP session. */
|
|
64
64
|
export type McpSessionState = Uninitialized | Initializing | readonly ['initialized', InitializedState];
|
|
65
|
+
/**
|
|
66
|
+
* The protocol revisions a server supports, **latest first** and non-empty by
|
|
67
|
+
* construction — the tuple's head is what an unsupported request is answered
|
|
68
|
+
* with, so "the server supports nothing" is not a state that can be reached.
|
|
69
|
+
*/
|
|
70
|
+
export type ProtocolVersions = readonly [string, ...readonly string[]];
|
|
65
71
|
/** Static configuration supplied by the server implementer. */
|
|
66
72
|
export type McpConfig = {
|
|
67
73
|
readonly serverInfo: Implementation;
|
|
68
74
|
readonly capabilities: ServerCapabilities;
|
|
69
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Every revision this server speaks, latest first. `initialize` answers
|
|
77
|
+
* with the client's requested version when it is in this list and with the
|
|
78
|
+
* head — the latest supported one — otherwise, which is the counter-proposal
|
|
79
|
+
* the lifecycle spec prescribes.
|
|
80
|
+
*/
|
|
81
|
+
readonly protocolVersions: ProtocolVersions;
|
|
70
82
|
};
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
isValidCodePoint,
|
|
20
20
|
} from '../code_point/module.f.mjs'
|
|
21
21
|
import { msb, u8List, length } from '../../types/bit_vec/module.f.mjs'
|
|
22
|
+
import { contains } from '../../types/range/module.f.mjs'
|
|
22
23
|
import { codePointListToString } from '../utf16/module.f.mjs'
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -43,6 +44,31 @@ const lead3Mask = 0b0000_1111
|
|
|
43
44
|
const lead4Tag = 0b1111_0000
|
|
44
45
|
const lead4Mask = 0b0000_0111
|
|
45
46
|
|
|
47
|
+
const isInU8Range = contains(0x00, 0xff)
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Whether `i` is a byte this decoder can be handed.
|
|
51
|
+
*
|
|
52
|
+
* `U8` is just `number`, so neither half is redundant. The dispatch below
|
|
53
|
+
* partitions only the *integers* in `0x00`–`0xff` — below `contTag`, a
|
|
54
|
+
* continuation, or one of the leads, with no gap — so a fraction falls between
|
|
55
|
+
* two of those and would be misclassified rather than rejected: emitted as a
|
|
56
|
+
* code point when it is below `contTag`, tagged with a fractional payload when
|
|
57
|
+
* it is not. Worse where the payload arithmetic reaches it, since the bitwise
|
|
58
|
+
* operators truncate silently: a fractional continuation byte would decode to
|
|
59
|
+
* the very code point its integer part spells, reporting nothing.
|
|
60
|
+
*
|
|
61
|
+
* The range half is not carrying `NaN` — `contains` is written positively, so
|
|
62
|
+
* `isInU8Range(NaN)` is already `false`. `Number.isInteger`'s own job here is
|
|
63
|
+
* the *in-range* fraction, which no bound can reach.
|
|
64
|
+
*
|
|
65
|
+
* This mirrors `u16` in `../utf16/module.f.mjs`, which carries the same check
|
|
66
|
+
* for the same reason; the two differ only in their bounds.
|
|
67
|
+
*
|
|
68
|
+
* @type {(i: number) => boolean}
|
|
69
|
+
*/
|
|
70
|
+
const u8 = i => Number.isInteger(i) && isInU8Range(i)
|
|
71
|
+
|
|
46
72
|
/**
|
|
47
73
|
* Encodes the low six bits of `x` as a UTF-8 continuation byte.
|
|
48
74
|
*
|
|
@@ -199,7 +225,7 @@ export const utf8StateToError = state => {
|
|
|
199
225
|
* @type {StateScan<number, Utf8State, readonly I32[]>}
|
|
200
226
|
*/
|
|
201
227
|
export const utf8ByteToCodePointOp = (byte, state) => {
|
|
202
|
-
if (byte
|
|
228
|
+
if (!u8(byte)) {
|
|
203
229
|
return [[errorMask], state]
|
|
204
230
|
}
|
|
205
231
|
if (state === null) return restart([])(byte)
|
|
@@ -82,6 +82,43 @@ export const proof = {
|
|
|
82
82
|
() => {
|
|
83
83
|
const result = stringify(utf8ByteToCodePointOp(0x80, [0xf8, 0x80]))
|
|
84
84
|
assertEq(result, '[[-2147483136,-2147483520],null]')
|
|
85
|
+
},
|
|
86
|
+
// `U8` is just `number`, so a non-integer in [0x00, 0xff] is a
|
|
87
|
+
// possible (if malformed) input. The dispatch partitions only the
|
|
88
|
+
// integers in that range, so a fraction has to be rejected here rather
|
|
89
|
+
// than misclassified: below `contTag` it would otherwise be emitted as
|
|
90
|
+
// a code point, and above it tagged with a fractional payload.
|
|
91
|
+
() => {
|
|
92
|
+
const result = stringify(toArray(toCodePointList([1.5])))
|
|
93
|
+
assertEq(result, '[2147483648]')
|
|
94
|
+
},
|
|
95
|
+
() => {
|
|
96
|
+
const result = stringify(toArray(toCodePointList([200.5])))
|
|
97
|
+
assertEq(result, '[2147483648]')
|
|
98
|
+
},
|
|
99
|
+
// `NaN` is flagged either way — the old guard let it past and the
|
|
100
|
+
// payload arithmetic tagged it anyway, as `errorMask | 0`. Pinned for
|
|
101
|
+
// the spelling: rejecting it up front gives the canonical `errorMask`
|
|
102
|
+
// rather than its `| 0` form.
|
|
103
|
+
() => {
|
|
104
|
+
const result = stringify(toArray(toCodePointList([NaN])))
|
|
105
|
+
assertEq(result, '[2147483648]')
|
|
106
|
+
},
|
|
107
|
+
// A fractional continuation byte is the case that hides: the bitwise
|
|
108
|
+
// payload arithmetic truncates it, so `C3 A9.5` decoded as U+00E9 —
|
|
109
|
+
// the same character `C3 A9` spells — with nothing reported. It is now
|
|
110
|
+
// invalid on its own, and the pending lead is flagged incomplete.
|
|
111
|
+
() => {
|
|
112
|
+
const result = stringify(toArray(toCodePointList([0xc3, 0xa9])))
|
|
113
|
+
assertEq(result, '[233]')
|
|
114
|
+
},
|
|
115
|
+
() => {
|
|
116
|
+
const result = stringify(toArray(toCodePointList([0xc3, 169.5])))
|
|
117
|
+
assertEq(result, '[2147483648,-2147483453]')
|
|
118
|
+
},
|
|
119
|
+
() => {
|
|
120
|
+
const result = stringify(toArray(toCodePointList([0xf0, 0x90, 128.5])))
|
|
121
|
+
assertEq(result, '[2147483648,-2147483120]')
|
|
85
122
|
}
|
|
86
123
|
],
|
|
87
124
|
fromCodePointList: [
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @import { Tuple } from './types.ts'
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { fromUndefined
|
|
9
|
+
import { fromUndefined } from '../nullable/module.f.mjs'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @type {(value: unknown) => value is readonly unknown[]}
|
|
@@ -61,6 +61,11 @@ export const last = a => at(a.length - 1)(a)
|
|
|
61
61
|
* through `fromUndefined`, so it reads a *stored* `null` or `undefined`
|
|
62
62
|
* element as absence. `[null]` is not empty, but `first` says it is.
|
|
63
63
|
*
|
|
64
|
+
* For the same reason `splitFirst`/`splitLast` take their element by index
|
|
65
|
+
* rather than projecting `first`/`last` through `nullable`'s `map`: their
|
|
66
|
+
* result tells "present and nullish" apart from "empty" — `[null, []]` is not
|
|
67
|
+
* `null` — where `at`/`first`/`last`, answering `T | null`, cannot.
|
|
68
|
+
*
|
|
64
69
|
* @type {<T, R>(f: (a: readonly T[]) => R) => (a: readonly T[]) => R | null}
|
|
65
70
|
*/
|
|
66
71
|
const onNonEmpty = f => a => a.length === 0 ? null : f(a)
|
|
@@ -70,24 +75,26 @@ const onNonEmpty = f => a => a.length === 0 ? null : f(a)
|
|
|
70
75
|
*/
|
|
71
76
|
export const tail = onNonEmpty(uncheckTail)
|
|
72
77
|
|
|
78
|
+
/**
|
|
79
|
+
* @type {<T>(a: readonly T[]) => readonly [T, readonly T[]]}
|
|
80
|
+
*/
|
|
81
|
+
const uncheckSplitFirst = a => [a[0], uncheckTail(a)]
|
|
82
|
+
|
|
73
83
|
/**
|
|
74
84
|
* @type {<T>(a: readonly T[]) => readonly [T, readonly T[]] | null}
|
|
75
85
|
*/
|
|
76
|
-
export const splitFirst =
|
|
77
|
-
/** @typedef {(typeof a)[0]} T */
|
|
78
|
-
const split = (/** @type {T} */first) =>
|
|
79
|
-
/** @type {const} */([first, uncheckTail(a)])
|
|
80
|
-
return map(split)(first(a))
|
|
81
|
-
}
|
|
86
|
+
export const splitFirst = onNonEmpty(uncheckSplitFirst)
|
|
82
87
|
|
|
83
88
|
/** @type {<T>(a: readonly T[]) => readonly T[] | null} */
|
|
84
89
|
export const head = onNonEmpty(uncheckHead)
|
|
85
90
|
|
|
91
|
+
/**
|
|
92
|
+
* @type {<T>(a: readonly T[]) => readonly [readonly T[], T]}
|
|
93
|
+
*/
|
|
94
|
+
const uncheckSplitLast = a => [uncheckHead(a), a[a.length - 1]]
|
|
95
|
+
|
|
86
96
|
/** @type {<T>(a: readonly T[]) => readonly [readonly T[], T] | null} */
|
|
87
|
-
export const splitLast =
|
|
88
|
-
const lastA = last(a)
|
|
89
|
-
return lastA === null ? null : [uncheckHead(a), lastA]
|
|
90
|
-
}
|
|
97
|
+
export const splitLast = onNonEmpty(uncheckSplitLast)
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
100
|
* An empty immutable array.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { at, first, last, head, tail, splitFirst, splitLast, empty } from './module.f.mjs'
|
|
2
2
|
import { stringify as jsonStringify } from '../../media/json/module.f.mjs'
|
|
3
3
|
import { sort } from '../object/module.f.mjs'
|
|
4
|
-
import { assertEq, assertNotNullish } from '../../asserts/module.f.mjs'
|
|
4
|
+
import { assertEq, assertNotNullish, assertStructurallySame } from '../../asserts/module.f.mjs'
|
|
5
5
|
|
|
6
6
|
const stringify = jsonStringify(sort)
|
|
7
7
|
|
|
@@ -74,6 +74,20 @@ export const proof = {
|
|
|
74
74
|
const result = splitFirst([])
|
|
75
75
|
assertEq(result, null)
|
|
76
76
|
},
|
|
77
|
+
// A stored nullish element is an element: `[null]` has a first one,
|
|
78
|
+
// even though `first([null])` cannot say so.
|
|
79
|
+
() => {
|
|
80
|
+
const result = splitFirst([null])
|
|
81
|
+
assertStructurallySame(result, [null, []])
|
|
82
|
+
},
|
|
83
|
+
() => {
|
|
84
|
+
const result = splitFirst([undefined])
|
|
85
|
+
assertStructurallySame(result, [undefined, []])
|
|
86
|
+
},
|
|
87
|
+
() => {
|
|
88
|
+
const result = splitFirst([undefined, 20, 300])
|
|
89
|
+
assertStructurallySame(result, [undefined, [20, 300]])
|
|
90
|
+
},
|
|
77
91
|
],
|
|
78
92
|
splitLast: [
|
|
79
93
|
() => {
|
|
@@ -84,6 +98,18 @@ export const proof = {
|
|
|
84
98
|
() => {
|
|
85
99
|
const result = splitLast([])
|
|
86
100
|
assertEq(result, null)
|
|
101
|
+
},
|
|
102
|
+
() => {
|
|
103
|
+
const result = splitLast([null])
|
|
104
|
+
assertStructurallySame(result, [[], null])
|
|
105
|
+
},
|
|
106
|
+
() => {
|
|
107
|
+
const result = splitLast([undefined])
|
|
108
|
+
assertStructurallySame(result, [[], undefined])
|
|
109
|
+
},
|
|
110
|
+
() => {
|
|
111
|
+
const result = splitLast([1, 20, undefined])
|
|
112
|
+
assertStructurallySame(result, [[1, 20], undefined])
|
|
87
113
|
}
|
|
88
114
|
],
|
|
89
115
|
empty: () => {
|
|
@@ -3,12 +3,28 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*
|
|
6
|
-
* @import { BigFloat } from './types.ts'
|
|
6
|
+
* @import { BigFloat, Format } from './types.ts'
|
|
7
|
+
* @import { Nullable } from '../nullable/types.ts'
|
|
7
8
|
*/
|
|
8
|
-
import type { BigFloat } from './types.ts';
|
|
9
|
+
import type { BigFloat, Format } from './types.ts';
|
|
10
|
+
import type { Nullable } from '../nullable/types.ts';
|
|
9
11
|
export type _BigFloatWithRemainder = readonly [BigFloat, bigint];
|
|
10
12
|
/** @type {(_: BigFloat) => (mul: bigint) => BigFloat} */
|
|
11
13
|
export declare const multiply: (_: BigFloat) => (mul: bigint) => BigFloat;
|
|
14
|
+
/**
|
|
15
|
+
* IEEE-754 binary64 — the format of a JavaScript `number`.
|
|
16
|
+
*
|
|
17
|
+
* Both exponents are *ulp* exponents, the same units a `BigFloat`'s own
|
|
18
|
+
* exponent is in: `minExp` is the exponent of the smallest subnormal
|
|
19
|
+
* (`2^-1074`), not the smallest normal's `-1022`, and `maxExp` is the exponent
|
|
20
|
+
* the largest finite value carries once its mantissa fills `precision` bits
|
|
21
|
+
* (`(2^53 - 1) * 2^971`). The two conventions differ by the precision, and a
|
|
22
|
+
* `Format` that mixes them is off by exactly that much, so this one never
|
|
23
|
+
* leaves `BigFloat`'s units.
|
|
24
|
+
*
|
|
25
|
+
* @type {Format}
|
|
26
|
+
*/
|
|
27
|
+
export declare const binary64: Format;
|
|
12
28
|
/**
|
|
13
29
|
* Converts a decimal big-float `m * 10^e` into the nearest binary big-float,
|
|
14
30
|
* rounding ties to even.
|
|
@@ -18,17 +34,40 @@ export declare const multiply: (_: BigFloat) => (mul: bigint) => BigFloat;
|
|
|
18
34
|
* even when rounding carries out of the top bit.
|
|
19
35
|
*
|
|
20
36
|
* The **exponent is unbounded**: this is the correctly-rounded 53-bit value,
|
|
21
|
-
* not a `number`.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* twice and can land an ulp
|
|
26
|
-
*
|
|
27
|
-
*
|
|
37
|
+
* not a `number`. A result too large for a `double` is not turned into an
|
|
38
|
+
* infinity, and one below the normal range keeps all 53 bits instead of the
|
|
39
|
+
* fewer a subnormal carries. That is the honest answer when the target is not
|
|
40
|
+
* a `double` — and the wrong starting point when it is, because rounding one
|
|
41
|
+
* of these results onto the subnormal grid rounds twice and can land an ulp
|
|
42
|
+
* away from the correctly-rounded `double`. Use {@link tryDecToFormat} with
|
|
43
|
+
* {@link binary64} for that; it rounds once.
|
|
28
44
|
*
|
|
29
45
|
* @type {(dec: BigFloat) => BigFloat}
|
|
30
46
|
*/
|
|
31
47
|
export declare const decToBin: (dec: BigFloat) => BigFloat;
|
|
48
|
+
/**
|
|
49
|
+
* Converts a decimal big-float `m * 10^e` into the nearest value of `format`,
|
|
50
|
+
* rounding ties to even, or `null` when the magnitude is too large for the
|
|
51
|
+
* format to hold — the caller decides what an overflow becomes, since
|
|
52
|
+
* `BigFloat` has no encoding for an infinity.
|
|
53
|
+
*
|
|
54
|
+
* The result is a multiple of `2^format.minExp` carrying **at most**
|
|
55
|
+
* `format.precision` significant bits: exactly that many above the format's
|
|
56
|
+
* normal range, fewer below it, and `[0n, 0]` when the value rounds away
|
|
57
|
+
* entirely. Underflow to zero is not a separate signal, because `[0n, 0]` is
|
|
58
|
+
* the correctly-rounded answer rather than a failure to produce one.
|
|
59
|
+
*
|
|
60
|
+
* The rounding happens **once**, on the exact decimal, which is the whole
|
|
61
|
+
* point of taking the format rather than post-processing {@link decToBin}. The
|
|
62
|
+
* grid it rounds onto is `2^max(minExp, e + 1)`, where `e + 1` is the exponent
|
|
63
|
+
* a full-precision result would carry: full precision above the normal range's
|
|
64
|
+
* floor, shrinking by a bit per binade below it, down to none at all. Rounding
|
|
65
|
+
* to `precision` bits first and onto that grid afterwards is two roundings,
|
|
66
|
+
* and the first can manufacture a midpoint the true value only approached.
|
|
67
|
+
*
|
|
68
|
+
* @type {(format: Format) => (dec: BigFloat) => Nullable<BigFloat>}
|
|
69
|
+
*/
|
|
70
|
+
export declare const tryDecToFormat: (format: Format) => (dec: BigFloat) => Nullable<BigFloat>;
|
|
32
71
|
export declare const proof: {
|
|
33
72
|
normalizeMantissaZero: () => void;
|
|
34
73
|
};
|