aontu 0.52.1 → 0.54.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 (310) hide show
  1. package/README.md +88 -0
  2. package/bin/aontu-mcp.js +4 -0
  3. package/dist/agentsmd.d.ts +16 -0
  4. package/dist/agentsmd.js +107 -0
  5. package/dist/agentsmd.js.map +1 -0
  6. package/dist/aontu.d.ts +15 -3
  7. package/dist/aontu.js +126 -5
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +45 -1
  10. package/dist/cli.js +2845 -69
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +18 -0
  13. package/dist/ctx.js +44 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/diff.d.ts +22 -0
  16. package/dist/diff.js +141 -0
  17. package/dist/diff.js.map +1 -0
  18. package/dist/err.d.ts +3 -1
  19. package/dist/err.js +45 -8
  20. package/dist/err.js.map +1 -1
  21. package/dist/graph.d.ts +13 -0
  22. package/dist/graph.js +110 -0
  23. package/dist/graph.js.map +1 -0
  24. package/dist/hcanon.d.ts +3 -0
  25. package/dist/hcanon.js +145 -0
  26. package/dist/hcanon.js.map +1 -0
  27. package/dist/hints.js +235 -8
  28. package/dist/hints.js.map +1 -1
  29. package/dist/jsonschema.d.ts +20 -0
  30. package/dist/jsonschema.js +425 -0
  31. package/dist/jsonschema.js.map +1 -0
  32. package/dist/lang.js +955 -110
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +343 -48
  36. package/dist/lsp.js.map +1 -1
  37. package/dist/mcp-server.d.ts +20 -0
  38. package/dist/mcp-server.js +147 -0
  39. package/dist/mcp-server.js.map +1 -0
  40. package/dist/mcp.d.ts +42 -0
  41. package/dist/mcp.js +941 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mod-tool.d.ts +58 -0
  44. package/dist/mod-tool.js +532 -0
  45. package/dist/mod-tool.js.map +1 -0
  46. package/dist/mod.d.ts +35 -0
  47. package/dist/mod.js +345 -0
  48. package/dist/mod.js.map +1 -0
  49. package/dist/patch.d.ts +49 -0
  50. package/dist/patch.js +506 -0
  51. package/dist/patch.js.map +1 -0
  52. package/dist/provenance.d.ts +41 -0
  53. package/dist/provenance.js +336 -0
  54. package/dist/provenance.js.map +1 -0
  55. package/dist/query.d.ts +27 -0
  56. package/dist/query.js +294 -0
  57. package/dist/query.js.map +1 -0
  58. package/dist/reach.d.ts +15 -0
  59. package/dist/reach.js +168 -0
  60. package/dist/reach.js.map +1 -0
  61. package/dist/relation.d.ts +23 -0
  62. package/dist/relation.js +230 -0
  63. package/dist/relation.js.map +1 -0
  64. package/dist/report-sarif.d.ts +14 -0
  65. package/dist/report-sarif.js +102 -0
  66. package/dist/report-sarif.js.map +1 -0
  67. package/dist/sig.d.ts +25 -0
  68. package/dist/sig.js +277 -0
  69. package/dist/sig.js.map +1 -0
  70. package/dist/sigdecl.d.ts +2 -0
  71. package/dist/sigdecl.js +11 -0
  72. package/dist/sigdecl.js.map +1 -0
  73. package/dist/siggate.d.ts +4 -0
  74. package/dist/siggate.js +90 -0
  75. package/dist/siggate.js.map +1 -0
  76. package/dist/site.d.ts +4 -0
  77. package/dist/site.js +31 -0
  78. package/dist/site.js.map +1 -1
  79. package/dist/std.d.ts +1 -0
  80. package/dist/std.js +129 -0
  81. package/dist/std.js.map +1 -0
  82. package/dist/subsume.d.ts +39 -0
  83. package/dist/subsume.js +573 -0
  84. package/dist/subsume.js.map +1 -0
  85. package/dist/trim.d.ts +19 -0
  86. package/dist/trim.js +155 -0
  87. package/dist/trim.js.map +1 -0
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/dist/type.d.ts +17 -1
  90. package/dist/type.js.map +1 -1
  91. package/dist/unify.d.ts +2 -1
  92. package/dist/unify.js +234 -38
  93. package/dist/unify.js.map +1 -1
  94. package/dist/utility.d.ts +8 -1
  95. package/dist/utility.js +68 -1
  96. package/dist/utility.js.map +1 -1
  97. package/dist/val/AggFuncVal.d.ts +44 -0
  98. package/dist/val/AggFuncVal.js +364 -0
  99. package/dist/val/AggFuncVal.js.map +1 -0
  100. package/dist/val/ArithFuncVal.d.ts +31 -0
  101. package/dist/val/ArithFuncVal.js +62 -0
  102. package/dist/val/ArithFuncVal.js.map +1 -0
  103. package/dist/val/BagVal.d.ts +7 -0
  104. package/dist/val/BagVal.js +150 -11
  105. package/dist/val/BagVal.js.map +1 -1
  106. package/dist/val/CloseFuncVal.js +9 -1
  107. package/dist/val/CloseFuncVal.js.map +1 -1
  108. package/dist/val/ConjunctVal.d.ts +1 -1
  109. package/dist/val/ConjunctVal.js +19 -0
  110. package/dist/val/ConjunctVal.js.map +1 -1
  111. package/dist/val/ConstraintVal.d.ts +7 -1
  112. package/dist/val/ConstraintVal.js +523 -45
  113. package/dist/val/ConstraintVal.js.map +1 -1
  114. package/dist/val/ContainerKindVal.d.ts +35 -0
  115. package/dist/val/ContainerKindVal.js +99 -0
  116. package/dist/val/ContainerKindVal.js.map +1 -0
  117. package/dist/val/CopyFuncVal.d.ts +1 -2
  118. package/dist/val/CopyFuncVal.js.map +1 -1
  119. package/dist/val/Decimal.d.ts +1 -0
  120. package/dist/val/Decimal.js +13 -0
  121. package/dist/val/Decimal.js.map +1 -1
  122. package/dist/val/DeprecateFuncVal.d.ts +11 -0
  123. package/dist/val/DeprecateFuncVal.js +47 -0
  124. package/dist/val/DeprecateFuncVal.js.map +1 -0
  125. package/dist/val/DisjunctVal.d.ts +1 -2
  126. package/dist/val/DisjunctVal.js +269 -53
  127. package/dist/val/DisjunctVal.js.map +1 -1
  128. package/dist/val/EachFuncVal.d.ts +15 -0
  129. package/dist/val/EachFuncVal.js +75 -0
  130. package/dist/val/EachFuncVal.js.map +1 -0
  131. package/dist/val/ExpectVal.js +62 -4
  132. package/dist/val/ExpectVal.js.map +1 -1
  133. package/dist/val/FeatureVal.js +1 -1
  134. package/dist/val/FeatureVal.js.map +1 -1
  135. package/dist/val/FilterFuncVal.d.ts +15 -0
  136. package/dist/val/FilterFuncVal.js +91 -0
  137. package/dist/val/FilterFuncVal.js.map +1 -0
  138. package/dist/val/FuncBaseVal.d.ts +7 -1
  139. package/dist/val/FuncBaseVal.js +183 -2
  140. package/dist/val/FuncBaseVal.js.map +1 -1
  141. package/dist/val/GraphAtomVal.d.ts +39 -0
  142. package/dist/val/GraphAtomVal.js +184 -0
  143. package/dist/val/GraphAtomVal.js.map +1 -0
  144. package/dist/val/HideFuncVal.js.map +1 -1
  145. package/dist/val/JunctionVal.js +24 -1
  146. package/dist/val/JunctionVal.js.map +1 -1
  147. package/dist/val/KeyFuncVal.d.ts +1 -1
  148. package/dist/val/KeyFuncVal.js +38 -30
  149. package/dist/val/KeyFuncVal.js.map +1 -1
  150. package/dist/val/ListVal.js +94 -14
  151. package/dist/val/ListVal.js.map +1 -1
  152. package/dist/val/LowerFuncVal.js.map +1 -1
  153. package/dist/val/MapVal.d.ts +1 -0
  154. package/dist/val/MapVal.js +146 -14
  155. package/dist/val/MapVal.js.map +1 -1
  156. package/dist/val/MatchFuncVal.d.ts +15 -0
  157. package/dist/val/MatchFuncVal.js +107 -0
  158. package/dist/val/MatchFuncVal.js.map +1 -0
  159. package/dist/val/MoveFuncVal.js.map +1 -1
  160. package/dist/val/NilVal.js +24 -0
  161. package/dist/val/NilVal.js.map +1 -1
  162. package/dist/val/OpBaseVal.d.ts +1 -1
  163. package/dist/val/OpBaseVal.js +19 -1
  164. package/dist/val/OpBaseVal.js.map +1 -1
  165. package/dist/val/OpenFuncVal.js +4 -1
  166. package/dist/val/OpenFuncVal.js.map +1 -1
  167. package/dist/val/PackFuncVal.d.ts +15 -0
  168. package/dist/val/PackFuncVal.js +108 -0
  169. package/dist/val/PackFuncVal.js.map +1 -0
  170. package/dist/val/PathFuncVal.d.ts +2 -2
  171. package/dist/val/PathFuncVal.js +75 -16
  172. package/dist/val/PathFuncVal.js.map +1 -1
  173. package/dist/val/PathVal.d.ts +25 -0
  174. package/dist/val/PathVal.js +150 -0
  175. package/dist/val/PathVal.js.map +1 -0
  176. package/dist/val/PlaceVal.d.ts +13 -0
  177. package/dist/val/PlaceVal.js +131 -0
  178. package/dist/val/PlaceVal.js.map +1 -0
  179. package/dist/val/PlusOpVal.d.ts +2 -1
  180. package/dist/val/PlusOpVal.js +61 -37
  181. package/dist/val/PlusOpVal.js.map +1 -1
  182. package/dist/val/PrefFuncVal.js.map +1 -1
  183. package/dist/val/PrefVal.d.ts +4 -2
  184. package/dist/val/PrefVal.js +223 -41
  185. package/dist/val/PrefVal.js.map +1 -1
  186. package/dist/val/RecurseVal.d.ts +19 -0
  187. package/dist/val/RecurseVal.js +217 -0
  188. package/dist/val/RecurseVal.js.map +1 -0
  189. package/dist/val/RefVal.d.ts +3 -2
  190. package/dist/val/RefVal.js +181 -66
  191. package/dist/val/RefVal.js.map +1 -1
  192. package/dist/val/ReferFuncVal.d.ts +59 -0
  193. package/dist/val/ReferFuncVal.js +604 -0
  194. package/dist/val/ReferFuncVal.js.map +1 -0
  195. package/dist/val/ScalarKindVal.d.ts +4 -3
  196. package/dist/val/ScalarKindVal.js +12 -12
  197. package/dist/val/ScalarKindVal.js.map +1 -1
  198. package/dist/val/SuperFuncVal.d.ts +4 -2
  199. package/dist/val/SuperFuncVal.js +118 -14
  200. package/dist/val/SuperFuncVal.js.map +1 -1
  201. package/dist/val/TopVal.d.ts +1 -1
  202. package/dist/val/TopVal.js.map +1 -1
  203. package/dist/val/TypeFuncVal.js.map +1 -1
  204. package/dist/val/UpperFuncVal.js.map +1 -1
  205. package/dist/val/Val.d.ts +7 -1
  206. package/dist/val/Val.js +170 -2
  207. package/dist/val/Val.js.map +1 -1
  208. package/dist/val/VarVal.js.map +1 -1
  209. package/dist/val/arith.d.ts +6 -0
  210. package/dist/val/arith.js +173 -0
  211. package/dist/val/arith.js.map +1 -0
  212. package/dist/vet.d.ts +45 -0
  213. package/dist/vet.js +802 -0
  214. package/dist/vet.js.map +1 -0
  215. package/dist/view.d.ts +85 -0
  216. package/dist/view.js +1948 -0
  217. package/dist/view.js.map +1 -0
  218. package/dist/walk.d.ts +2 -0
  219. package/dist/walk.js +91 -0
  220. package/dist/walk.js.map +1 -0
  221. package/grammar/aontu.gbnf +138 -0
  222. package/grammar/aontu.lark +118 -0
  223. package/grammar/aontu.tmLanguage.json +184 -0
  224. package/package.json +30 -8
  225. package/skill/SKILL.md +37 -0
  226. package/skill/error-codes.md +62 -0
  227. package/skill/examples.md +99 -0
  228. package/skill/grammar-card.md +56 -0
  229. package/src/agentsmd.ts +135 -0
  230. package/src/aontu.ts +166 -5
  231. package/src/cli.ts +3324 -72
  232. package/src/ctx.ts +93 -1
  233. package/src/diff.ts +198 -0
  234. package/src/err.ts +50 -8
  235. package/src/graph.ts +185 -0
  236. package/src/hcanon.ts +167 -0
  237. package/src/hints.ts +291 -8
  238. package/src/jsonschema.ts +552 -0
  239. package/src/lang.ts +1049 -110
  240. package/src/lsp.ts +351 -49
  241. package/src/mcp-server.ts +187 -0
  242. package/src/mcp.ts +1130 -0
  243. package/src/mod-tool.ts +718 -0
  244. package/src/mod.ts +453 -0
  245. package/src/patch.ts +628 -0
  246. package/src/provenance.ts +437 -0
  247. package/src/query.ts +381 -0
  248. package/src/reach.ts +213 -0
  249. package/src/relation.ts +298 -0
  250. package/src/report-sarif.ts +137 -0
  251. package/src/sig.ts +345 -0
  252. package/src/sigdecl.ts +11 -0
  253. package/src/siggate.ts +144 -0
  254. package/src/site.ts +36 -1
  255. package/src/std.ts +131 -0
  256. package/src/subsume.ts +739 -0
  257. package/src/trim.ts +195 -0
  258. package/src/tsconfig.json +10 -4
  259. package/src/type.ts +51 -2
  260. package/src/unify.ts +253 -38
  261. package/src/utility.ts +79 -1
  262. package/src/val/AggFuncVal.ts +546 -0
  263. package/src/val/ArithFuncVal.ts +108 -0
  264. package/src/val/BagVal.ts +158 -12
  265. package/src/val/CloseFuncVal.ts +9 -1
  266. package/src/val/ConjunctVal.ts +20 -0
  267. package/src/val/ConstraintVal.ts +576 -48
  268. package/src/val/ContainerKindVal.ts +158 -0
  269. package/src/val/CopyFuncVal.ts +0 -1
  270. package/src/val/Decimal.ts +15 -0
  271. package/src/val/DeprecateFuncVal.ts +84 -0
  272. package/src/val/DisjunctVal.ts +282 -59
  273. package/src/val/EachFuncVal.ts +133 -0
  274. package/src/val/ExpectVal.ts +65 -8
  275. package/src/val/FeatureVal.ts +1 -1
  276. package/src/val/FilterFuncVal.ts +154 -0
  277. package/src/val/FuncBaseVal.ts +206 -2
  278. package/src/val/GraphAtomVal.ts +264 -0
  279. package/src/val/HideFuncVal.ts +0 -2
  280. package/src/val/JunctionVal.ts +24 -1
  281. package/src/val/KeyFuncVal.ts +39 -35
  282. package/src/val/ListVal.ts +101 -15
  283. package/src/val/LowerFuncVal.ts +0 -1
  284. package/src/val/MapVal.ts +153 -14
  285. package/src/val/MatchFuncVal.ts +176 -0
  286. package/src/val/MoveFuncVal.ts +0 -2
  287. package/src/val/NilVal.ts +25 -0
  288. package/src/val/OpBaseVal.ts +20 -1
  289. package/src/val/OpenFuncVal.ts +4 -2
  290. package/src/val/PackFuncVal.ts +175 -0
  291. package/src/val/PathFuncVal.ts +107 -20
  292. package/src/val/PathVal.ts +221 -0
  293. package/src/val/PlaceVal.ts +193 -0
  294. package/src/val/PlusOpVal.ts +67 -39
  295. package/src/val/PrefFuncVal.ts +0 -1
  296. package/src/val/PrefVal.ts +251 -60
  297. package/src/val/RecurseVal.ts +285 -0
  298. package/src/val/RefVal.ts +183 -78
  299. package/src/val/ReferFuncVal.ts +732 -0
  300. package/src/val/ScalarKindVal.ts +12 -13
  301. package/src/val/SuperFuncVal.ts +137 -13
  302. package/src/val/TopVal.ts +1 -2
  303. package/src/val/TypeFuncVal.ts +0 -2
  304. package/src/val/UpperFuncVal.ts +0 -1
  305. package/src/val/Val.ts +216 -4
  306. package/src/val/VarVal.ts +0 -1
  307. package/src/val/arith.ts +319 -0
  308. package/src/vet.ts +1025 -0
  309. package/src/view.ts +2563 -0
  310. package/src/walk.ts +99 -0
package/src/lsp.ts CHANGED
@@ -18,8 +18,12 @@
18
18
 
19
19
  import type { Val } from './type'
20
20
 
21
- import { Aontu } from './aontu'
21
+ import { Aontu, VERSION } from './aontu'
22
22
  import { getHint } from './err'
23
+ import { collectNils } from './walk'
24
+ import { collectDeprecations, deprecationMessage } from './utility'
25
+ import { why } from './query'
26
+ import type { WhyConjunct } from './provenance'
23
27
 
24
28
 
25
29
  // LSP DiagnosticSeverity subset.
@@ -28,8 +32,12 @@ const SEVERITY_WARNING = 2
28
32
  const SEVERITY_INFORMATION = 3
29
33
  const SEVERITY_HINT = 4
30
34
 
31
- // Reported to the client in the initialize response.
32
- const LSP_VERSION = '0.1.0'
35
+ // Reported to the client in the initialize response. It is the
36
+ // ENGINE's version, not a number of the server's own: a separately
37
+ // maintained one drifts, and had -- the server answered 0.1.0 against
38
+ // a package at 0.52.1, so a client could not tell which engine it was
39
+ // talking to (status-2026-08-21.md section 10).
40
+ const LSP_VERSION = VERSION
33
41
 
34
42
 
35
43
  // Zero-based line / UTF-16 character offset (LSP Position).
@@ -45,6 +53,9 @@ type Diagnostic = {
45
53
  code?: string
46
54
  source: string
47
55
  message: string
56
+ // LSP DiagnosticTag values; [1] is Unnecessary, [2] is Deprecated —
57
+ // the native tag editors strike through (G3 phase 4).
58
+ tags?: number[]
48
59
  }
49
60
 
50
61
 
@@ -73,9 +84,13 @@ type OutMessage = {
73
84
  // functions, syntax errors) produce diagnostics.
74
85
  function computeDiagnostics(
75
86
  src: string,
76
- opts?: { vars?: Record<string, Val> }
87
+ opts?: { vars?: Record<string, Val>, trust?: any }
77
88
  ): Diagnostic[] {
78
- const aontu = new Aontu()
89
+ // The trust profile (G5, docs/trust.md): the LSP is the
90
+ // highest-exposure surface — merely OPENING a hostile .aon file in an
91
+ // editor performs its reads — so the handler confines evaluation to
92
+ // the workspace root and threads the profile through here.
93
+ const aontu = new Aontu(null == opts?.trust ? {} : { trust: opts.trust })
79
94
 
80
95
  let root: any
81
96
  let ac: any
@@ -92,9 +107,10 @@ function computeDiagnostics(
92
107
  return [parseErrorDiagnostic(err)]
93
108
  }
94
109
 
95
- const nils: any[] = []
110
+ // The walk's `seen` set is reused below to dedup context errors
111
+ // against the nils already found in the tree.
96
112
  const seen = new Set()
97
- walkNils(root, nils, seen)
113
+ const nils: any[] = collectNils(root, seen)
98
114
 
99
115
  // Errors recorded on the context but not present in the tree — e.g. a
100
116
  // budget_passes exhaustion nil, which is about the whole evaluation
@@ -109,34 +125,35 @@ function computeDiagnostics(
109
125
  }
110
126
  }
111
127
 
112
- return nils.map(nilToDiagnostic)
113
- }
114
-
115
-
116
- // Walk a unified Val tree collecting every NilVal exactly once. A NilVal
117
- // in the result always represents an error; valid non-concrete values
118
- // (scalar kinds, refs, conjuncts) are never NilVals.
119
- function walkNils(v: any, out: any[], seen: Set<any>) {
120
- if (null == v || 'object' !== typeof v || true !== v.isVal) return
121
- if (seen.has(v)) return
122
- seen.add(v)
123
-
124
- if (v.isNil) {
125
- out.push(v)
126
- return
127
- }
128
-
129
- const peg = v.peg
130
- if (Array.isArray(peg)) {
131
- for (const c of peg) walkNils(c, out, seen)
132
- }
133
- else if (null != peg && 'object' === typeof peg) {
134
- for (const k in peg) walkNils(peg[k], out, seen)
128
+ const out = nils.map(nilToDiagnostic)
129
+
130
+ // Deprecation tags (G3 phase 4): every sited value carrying the
131
+ // deprecate() record — the declaration and, because the record rides
132
+ // meets and reference clones, every use resolving through it — gets
133
+ // the native Deprecated tag (2) at Hint severity, so editors strike
134
+ // it through without shouting.
135
+ for (const { val } of collectDeprecations(root)) {
136
+ const v: any = val
137
+ if (1 > (v.site?.row ?? -1) || 1 > (v.site?.col ?? -1)) {
138
+ continue
139
+ }
140
+ out.push({
141
+ range: {
142
+ start: { line: v.site.row - 1, character: v.site.col - 1 },
143
+ end: {
144
+ line: v.site.row - 1,
145
+ character: v.site.col - 1 + siteExtent(v),
146
+ },
147
+ },
148
+ severity: 4,
149
+ code: 'deprecated',
150
+ source: 'aontu',
151
+ message: deprecationMessage(v.deprecation),
152
+ tags: [2],
153
+ })
135
154
  }
136
155
 
137
- // Spread constraints live off-peg on Map/List Vals.
138
- const spreadCj = v.spread?.cj
139
- if (spreadCj) walkNils(spreadCj, out, seen)
156
+ return out
140
157
  }
141
158
 
142
159
 
@@ -169,9 +186,39 @@ function nilToDiagnostic(nil: any): Diagnostic {
169
186
 
170
187
 
171
188
  // Length (UTF-16 units, like LSP characters) of the offending value's
172
- // canonical form, used to size the diagnostic range (minimum 1).
189
+ // SOURCE TEXT, used to size the diagnostic range (minimum 1).
173
190
  function labelLength(nil: any): number {
174
- const c = nil.primary?.canon
191
+ return null == nil.primary ? 1 : siteExtent(nil.primary)
192
+ }
193
+
194
+
195
+ // The extent to underline for a value: its SOURCE TEXT's length, with
196
+ // the canon as the fallback and 1 as the floor.
197
+ //
198
+ // CANON IS NOT SOURCE TEXT, which is the whole point of Site.len:
199
+ // `0x1F` has canon `31`, so sizing by canon underlines two characters
200
+ // of a four-character literal — hovering `0x1F` highlighted `0x` and
201
+ // hovering `1F` answered nothing. The Go twin is the same fallback in
202
+ // go/check.go (Problem.Len, ValueSpan.Len), in bytes there because it
203
+ // is added to a byte offset before conversion.
204
+ //
205
+ // The fallback is for a value carrying no stamped span — one propagated
206
+ // onto a result rather than written by a document — where canon is all
207
+ // there is and an approximate underline beats none. A REPORT never
208
+ // guesses this way: vet and why emit len only when it is known, because
209
+ // there a wrong length is a corrupted document rather than a wonky
210
+ // highlight.
211
+ function siteExtent(v: any): number {
212
+ const len = v?.site?.len
213
+ if ('number' === typeof len && len > 0) {
214
+ return len
215
+ }
216
+ // CANON IS READ DEFENSIVELY, as every other reader here does: it is a
217
+ // getter that unifies, and a host-supplied value can throw from it
218
+ // (hover-refuses-bad-input). A hover that cannot measure a value
219
+ // still has to answer for the rest of the document.
220
+ let c = ''
221
+ try { c = v?.canon } catch { c = '' }
175
222
  return 'string' === typeof c && c.length > 0 ? c.length : 1
176
223
  }
177
224
 
@@ -223,6 +270,7 @@ function initializeResult() {
223
270
  textDocumentSync: 1,
224
271
  hoverProvider: true,
225
272
  completionProvider: {},
273
+ signatureHelpProvider: { triggerCharacters: ['(', ','] },
226
274
  },
227
275
  serverInfo: {
228
276
  name: 'aontu-lsp',
@@ -232,6 +280,59 @@ function initializeResult() {
232
280
  }
233
281
 
234
282
 
283
+ // signatureHelp: the declared signature of the ENCLOSING call, served
284
+ // from the registry (docs/design/SIGNATURES.0.md). The enclosing call
285
+ // is found lexically -- scan back from the cursor for the nearest
286
+ // unclosed '(' and read the word before it; commas at that depth
287
+ // count the active parameter, capped at the last slot so a rest tail
288
+ // stays active for every excess argument. Strings are skipped so a
289
+ // paren or comma inside one does not miscount, and the scan stops at
290
+ // the line start, a call being one line in practice.
291
+ function computeSignatureHelp(text: string, pos: any): any {
292
+ // Position to offset, under the full-sync model: lines are exactly
293
+ // the text's newlines.
294
+ const lines = text.split('\n')
295
+ const line = Math.max(0, Math.min(Number(pos.line) || 0, lines.length - 1))
296
+ let offset = 0
297
+ for (let li = 0; li < line; li++) {
298
+ offset += lines[li].length + 1
299
+ }
300
+ offset += Math.max(0, Math.min(Number(pos.character) || 0, lines[line].length))
301
+ let depth = 0
302
+ let commas = 0
303
+ let open = -1
304
+ for (let i = offset - 1; 0 <= i; i--) {
305
+ const c = text[i]
306
+ if ('"' === c || "'" === c) {
307
+ for (i--; 0 <= i && text[i] !== c; i--) { }
308
+ continue
309
+ }
310
+ if (')' === c) { depth++ }
311
+ else if ('(' === c) {
312
+ if (0 === depth) { open = i; break }
313
+ depth--
314
+ }
315
+ else if (',' === c && 0 === depth) { commas++ }
316
+ else if ('\n' === c && 0 === depth) { break }
317
+ }
318
+ if (0 > open) { return null }
319
+ let start = open
320
+ while (0 < start && /[a-zA-Z0-9_]/.test(text[start - 1])) { start-- }
321
+ const name = text.slice(start, open)
322
+ const sig = funcSig[name]
323
+ if (undefined === sig) { return null }
324
+ const last = sig.args.length - 1
325
+ return {
326
+ signatures: [{
327
+ label: renderSig(sig),
328
+ parameters: sig.args.map((a) => ({ label: renderSigArg(a) })),
329
+ }],
330
+ activeSignature: 0,
331
+ activeParameter: Math.min(commas, 0 <= last ? last : 0),
332
+ }
333
+ }
334
+
335
+
235
336
  function publishDiagnosticsMsg(uri: string, diagnostics: Diagnostic[]): OutMessage {
236
337
  return {
237
338
  jsonrpc: '2.0',
@@ -251,10 +352,52 @@ type Hover = { contents: MarkupContent, range?: Range }
251
352
  // the position is not over a value with a known source location. Because
252
353
  // hover reads the *unified* tree, a literal shows its resolved value and
253
354
  // kind (e.g. a reference target resolves to the value it points at).
254
- function computeHover(src: string, position: Position): Hover | null {
355
+ // HOVER PROVENANCE (G7 phase 7) is CONFIG-GATED and off by default:
356
+ // the contributions that met at the hovered path, appended to the
357
+ // value's own hover. Hover already re-unifies the whole document per
358
+ // request, so an editor that asks for this pays a second instrumented
359
+ // evaluation knowingly, and one that does not pays nothing.
360
+ function provenanceMarkdown(
361
+ src: string, path: string[], trust?: any): string {
362
+ if (0 === path.length) {
363
+ return ''
364
+ }
365
+ // A document with an error ELSEWHERE still hovers — the tree the
366
+ // hover walked is there — while `why` refuses it, so the record may
367
+ // be absent for a value the cursor is sitting on.
368
+ const report = why(src, '$.' + path.join('.'), { trust } as any)
369
+ return contributionsMarkdown(report.record?.conjuncts ?? [])
370
+ }
371
+
372
+
373
+ // The contributions as hover markdown. Exported for the direct test
374
+ // (ADR-002): a siteless contribution and a named file are both shapes
375
+ // the record allows and no hover produces, hover evaluating one
376
+ // unnamed document.
377
+ export function contributionsMarkdown(conjuncts: WhyConjunct[]): string {
378
+ if (0 === conjuncts.length) {
379
+ return ''
380
+ }
381
+ return '\n\n---\n\nContributions:\n' + conjuncts.map((c) =>
382
+ '- `' + c.canon + '` — ' + c.role +
383
+ (0 > c.site.row ? '' : ' (' +
384
+ ('' === c.site.file ? '' : c.site.file + ':') +
385
+ c.site.row + ':' + c.site.col + ')')).join('\n')
386
+ }
387
+
388
+
389
+ // HOVER RUNS UNDER THE SAME CAPABILITY AS DIAGNOSTICS. It used to
390
+ // evaluate through `new Aontu()` -- the full system resolver -- BESIDE
391
+ // confined diagnostics in the same server, so a workspace-confined
392
+ // session still resolved an escaping include the moment a cursor rested
393
+ // on it (use-cases/REVIEW.md finding G). One document, two postures, is
394
+ // not a confinement.
395
+ function computeHover(
396
+ src: string, position: Position, provenance?: boolean,
397
+ trust?: any): Hover | null {
255
398
  let root: any
256
399
  try {
257
- root = new Aontu().unify(src, { collect: true })
400
+ root = new Aontu(null == trust ? {} : { trust }).unify(src, { collect: true })
258
401
  }
259
402
  catch {
260
403
  return null
@@ -274,7 +417,12 @@ function computeHover(src: string, position: Position): Hover | null {
274
417
  if (null == best) return null
275
418
 
276
419
  return {
277
- contents: { kind: 'markdown', value: hoverMarkdown(best.val) },
420
+ contents: {
421
+ kind: 'markdown',
422
+ value: hoverMarkdown(best.val) +
423
+ (true === provenance
424
+ ? provenanceMarkdown(src, best.val.path, trust) : ''),
425
+ },
278
426
  range: {
279
427
  start: { line: best.line, character: best.start },
280
428
  end: { line: best.line, character: best.end },
@@ -296,15 +444,24 @@ function collectHoverCandidates(
296
444
  const col = v.site.col
297
445
  let canon = ''
298
446
  try { canon = v.canon } catch { canon = '' }
447
+ // The span to highlight: the value's own source text where it has
448
+ // one, canon otherwise. See siteExtent.
449
+ const span = siteExtent(v)
450
+ const spanSrc = 'string' === typeof v.site?.src ? v.site.src : ''
299
451
  // Hover targets concrete values (scalars, kinds, refs, …), not
300
452
  // containers: a map/list source span is not reliably reconstructable
301
453
  // from a single site, and the same restriction in the Go port keeps
302
454
  // hover behaviour identical across implementations. The walk still
303
455
  // recurses into containers below to reach their leaf values. Canon is
304
456
  // single-line, so its length approximates the on-line source span.
305
- if (row >= 1 && col >= 1 && canon.length > 0 && !canon.includes('\n') &&
457
+ // Single-line is decided by the SOURCE TEXT when there is one: a
458
+ // multi-line token cannot be described by one line's start and end,
459
+ // and canon's newlines are not the token's. Falling back to canon
460
+ // keeps the old test for a value with no stamped span.
461
+ const multiline = '' === spanSrc ? canon.includes('\n') : spanSrc.includes('\n')
462
+ if (row >= 1 && col >= 1 && canon.length > 0 && !multiline &&
306
463
  !v.isMap && !v.isList) {
307
- out.push({ val: v, line: row - 1, start: col - 1, end: col - 1 + canon.length })
464
+ out.push({ val: v, line: row - 1, start: col - 1, end: col - 1 + span })
308
465
  }
309
466
 
310
467
  const peg = v.peg
@@ -354,16 +511,27 @@ type CompletionItem = {
354
511
  detail?: string
355
512
  }
356
513
 
514
+ import { funcSig, renderSig, renderSigArg } from './sig'
515
+
357
516
  // LSP CompletionItemKind subset.
358
517
  const COMPLETION_FUNCTION = 3
359
518
  const COMPLETION_KEYWORD = 14
360
519
 
361
- // The twenty-one built-in functions. Kept in sync with the engine by
520
+ // The built-in functions. Kept in sync with the engine by
362
521
  // `lsp.test.ts`, which asserts each is recognised and no others are.
522
+ // The Go port derives its list from the engine's own name set
523
+ // (`BuiltinFuncNames`, go/func.go), which is why a name added there
524
+ // and forgotten here diverges silently — as `id` and `refer` did
525
+ // between G4 phases 1/2 and G8 phase 1.
363
526
  const BUILTIN_FUNCS = [
364
- 'above', 'below', 'close', 'copy', 'hide', 'key', 'length', 'lower',
365
- 'max', 'min', 'move', 'must', 'neq', 'open', 'path', 'pref', 're',
366
- 'super', 'type', 'unique', 'upper',
527
+ 'above', 'acyclic', 'add', 'below', 'close', 'copy', 'deprecate', 'div',
528
+ 'each',
529
+ 'filter', 'greatest',
530
+ 'hide', 'inverse', 'join', 'key', 'least', 'length', 'list', 'lower',
531
+ 'map', 'match', 'max', 'min', 'mod', 'move', 'mul', 'must', 'neq', 'open',
532
+ 'pack', 'path', 'pick',
533
+ 'pref', 're', 'refer', 'rel', 'rem', 'sub', 'sum', 'super', 'type', 'unique',
534
+ 'upper',
367
535
  ]
368
536
 
369
537
  // Scalar-kind and literal keywords.
@@ -372,7 +540,9 @@ const BUILTIN_FUNCS = [
372
540
  const KIND_KEYWORDS = [
373
541
  'string', 'number', 'integer', 'float', 'biginteger', 'bigdecimal', 'boolean',
374
542
  ]
375
- const LITERAL_KEYWORDS = ['true', 'false', 'null', 'top']
543
+ // `_` joins these as of G8 phase 3: it is a literal of the language
544
+ // now, not text.
545
+ const LITERAL_KEYWORDS = ['_', 'true', 'false', 'null', 'top']
376
546
 
377
547
 
378
548
  // Context-free completion: the built-in functions, scalar-kind keywords
@@ -380,7 +550,10 @@ const LITERAL_KEYWORDS = ['true', 'false', 'null', 'top']
380
550
  function computeCompletions(): CompletionItem[] {
381
551
  const out: CompletionItem[] = []
382
552
  for (const f of BUILTIN_FUNCS) {
383
- out.push({ label: f, kind: COMPLETION_FUNCTION, detail: 'Aontu built-in function' })
553
+ // The detail is the rendered SIGNATURE (docs/design/SIGNATURES.0.md)
554
+ // -- the same renderer the hints and the docs table use, so the
555
+ // completion list cannot drift from the declaration.
556
+ out.push({ label: f, kind: COMPLETION_FUNCTION, detail: renderSig(funcSig[f]) })
384
557
  }
385
558
  for (const k of KIND_KEYWORDS) {
386
559
  out.push({ label: k, kind: COMPLETION_KEYWORD, detail: 'scalar kind' })
@@ -396,11 +569,103 @@ function computeCompletions(): CompletionItem[] {
396
569
  // messages and returns the messages to send back, tracking open document
397
570
  // text and recomputing diagnostics on open/change/close. Not safe for
398
571
  // concurrent use; drive it from a single loop (as the stdio server does).
572
+ // A file:// uri's filesystem path, for the workspace-root confinement.
573
+ // Percent-decoded; a non-file uri (or none) yields undefined.
574
+ //
575
+ // EXPORTED, inline as contributionsMarkdown is: part of the reusable
576
+ // LSP library surface, and the twin of the package-visible uriToPath in
577
+ // go/lsp/handler.go. Anything driving this module with its own
578
+ // transport has to turn a client's uri into a path the same way the
579
+ // confinement does, and the rules below are not guessable from outside.
580
+ //
581
+ // THE DRIVE-LETTER SLASH. A file uri names an absolute path after the
582
+ // authority, so on Windows the standard spelling every editor sends is
583
+ // file:///C:/Users/me/project — three slashes, and the third belongs to
584
+ // the PATH. Stripping only `file://` leaves `/C:/Users/me/project`,
585
+ // which is not a Windows path at all, so the workspace-root
586
+ // confinement below compared real paths against nonsense and an editor
587
+ // on Windows got no confinement it could rely on. Both ports carried
588
+ // the defect identically (go/lsp/handler.go uriToPath), and no test
589
+ // caught it because both ports' tests built the uri as `'file://' +
590
+ // path` — two slashes, which is not what a client sends and which
591
+ // accidentally produced a usable path.
592
+ //
593
+ // The leading slash is dropped only before a DRIVE LETTER, so a POSIX
594
+ // path keeps the root it needs: file:///tmp/x stays /tmp/x.
595
+ export function uriToPath(uri: unknown): string | undefined {
596
+ if ('string' !== typeof uri || !uri.startsWith('file://')) {
597
+ return undefined
598
+ }
599
+ const path = percentDecode(uri.slice('file://'.length))
600
+ // AN EMPTY PATH IS NOT A ROOT. `file://` on its own yields '', and
601
+ // '' is not nullish — so it won the `??` chain below and arrived as
602
+ // `{ include: { root: '' } }`, a confinement root that then resolves
603
+ // against the process working directory: the same client params made
604
+ // the server allow or deny an include depending on where it was
605
+ // started from. The Go twin never had it, because its chain tests
606
+ // `"" != folder` explicitly (go/lsp/handler.go). Answering undefined
607
+ // is what makes the two chains agree.
608
+ if ('' === path) {
609
+ return undefined
610
+ }
611
+ return driveLetterPath(path) ? path.slice(1) : path
612
+ }
613
+
614
+
615
+ // Percent-decoding that CANNOT THROW. `decodeURIComponent` raises a
616
+ // URIError on a malformed escape (`%ZZ`), and this runs on a uri a
617
+ // CLIENT sent — so a stray percent in a workspace path took the
618
+ // exception straight out of the initialize handler, where the Go twin
619
+ // swallowed the same failure and used the raw text
620
+ // (go/lsp/handler.go). Two ports, two behaviours, for an input neither
621
+ // of them controls.
622
+ //
623
+ // The agreement runs the other way for the SECOND way an escape can
624
+ // fail to decode. `%FF` is well-formed and names a raw byte — a
625
+ // perfectly good Linux filename — which Go produced and a JavaScript
626
+ // string cannot hold at all, so the ports derived different workspace
627
+ // roots for a uri a byte-oriented client really sends. Only one
628
+ // direction is reachable from both languages, so Go now declines to
629
+ // unescape a result that is not valid UTF-8 and both ports keep the
630
+ // raw text. An undecodable path is still a path, and refusing to serve
631
+ // a session over it helps nobody.
632
+ function percentDecode(text: string): string {
633
+ try {
634
+ return decodeURIComponent(text)
635
+ }
636
+ catch {
637
+ return text
638
+ }
639
+ }
640
+
641
+
642
+ // Whether p is `/X:…` for a drive letter X — the one shape whose
643
+ // leading slash is uri syntax rather than path. Mirrors the same
644
+ // predicate in go/lsp/handler.go.
645
+ function driveLetterPath(p: string): boolean {
646
+ return 3 <= p.length && '/' === p[0] && ':' === p[2] &&
647
+ /[A-Za-z]/.test(p[1])
648
+ }
649
+
650
+
399
651
  class LspHandler {
400
652
  private docs = new Map<string, string>()
401
653
  private shutdownOK = false
402
654
  private exited = false
403
655
 
656
+ // The trust profile evaluation runs under (G5, docs/trust.md):
657
+ // workspace-root confinement by default, set from the initialize
658
+ // params. An `initializationOptions.aontu.trust.include` of 'system',
659
+ // 'none' or { root } widens or narrows it explicitly. Undefined —
660
+ // no workspace root and no explicit option — falls back to today's
661
+ // unconfined behaviour, which single-file sessions rely on.
662
+ private trust: any = undefined
663
+
664
+ // Hover provenance (G7 phase 7): off unless an editor asks for it
665
+ // with `initializationOptions.aontu.provenance`. It costs a second,
666
+ // instrumented evaluation per hover, which is a cost to opt into.
667
+ private provenance = false
668
+
404
669
  // True once an `exit` notification has been received.
405
670
  get shouldExit(): boolean { return this.exited }
406
671
 
@@ -414,8 +679,34 @@ class LspHandler {
414
679
  // Process one incoming message, returning zero or more to send.
415
680
  handle(msg: Message): OutMessage[] {
416
681
  switch (msg.method) {
417
- case 'initialize':
682
+ case 'initialize': {
683
+ const params = msg.params ?? {}
684
+ this.provenance =
685
+ true === params.initializationOptions?.aontu?.provenance
686
+ const explicit = params.initializationOptions?.aontu?.trust?.include
687
+ if (null != explicit) {
688
+ // An explicit setting wins — validated, and an unrecognised
689
+ // value confines to NOTHING rather than silently widening:
690
+ // deny is the safe reading of a setting the server does not
691
+ // understand. The same rule as the Go handler.
692
+ this.trust =
693
+ 'system' === explicit ? undefined :
694
+ 'none' === explicit ? { include: 'none' } :
695
+ ('string' === typeof explicit?.root && '' !== explicit.root)
696
+ ? { include: { root: explicit.root } } :
697
+ (null != explicit?.mem && 'object' === typeof explicit.mem)
698
+ ? { include: { mem: explicit.mem } } :
699
+ { include: 'none' }
700
+ }
701
+ else {
702
+ const root = uriToPath(params.workspaceFolders?.[0]?.uri)
703
+ ?? uriToPath(params.rootUri)
704
+ ?? (('string' === typeof params.rootPath && '' !== params.rootPath)
705
+ ? params.rootPath : undefined)
706
+ this.trust = null != root ? { include: { root } } : undefined
707
+ }
418
708
  return [{ jsonrpc: '2.0', id: msg.id, result: initializeResult() }]
709
+ }
419
710
 
420
711
  case 'initialized':
421
712
  return []
@@ -456,13 +747,23 @@ class LspHandler {
456
747
  const uri = msg.params?.textDocument?.uri
457
748
  const pos = msg.params?.position
458
749
  const text = null != uri ? this.docs.get(uri) : undefined
459
- const hover = (null != text && null != pos) ? computeHover(text, pos) : null
750
+ const hover = (null != text && null != pos)
751
+ ? computeHover(text, pos, this.provenance, this.trust) : null
460
752
  return [{ jsonrpc: '2.0', id: msg.id, result: hover }]
461
753
  }
462
754
 
463
755
  case 'textDocument/completion':
464
756
  return [{ jsonrpc: '2.0', id: msg.id, result: computeCompletions() }]
465
757
 
758
+ case 'textDocument/signatureHelp': {
759
+ const uri = msg.params?.textDocument?.uri
760
+ const pos = msg.params?.position
761
+ const text = null != uri ? this.docs.get(uri) : undefined
762
+ const help = (null != text && null != pos)
763
+ ? computeSignatureHelp(text, pos) : null
764
+ return [{ jsonrpc: '2.0', id: msg.id, result: help }]
765
+ }
766
+
466
767
  default:
467
768
  // Unknown request (has an id): reply method-not-found. Unknown
468
769
  // notification: ignore.
@@ -478,7 +779,8 @@ class LspHandler {
478
779
  }
479
780
 
480
781
  private publish(uri: string): OutMessage {
481
- return publishDiagnosticsMsg(uri, computeDiagnostics(this.docs.get(uri) ?? ''))
782
+ return publishDiagnosticsMsg(uri,
783
+ computeDiagnostics(this.docs.get(uri) ?? '', { trust: this.trust }))
482
784
  }
483
785
  } /* node:coverage ignore next 28 */
484
786